diff --git a/Gruntfile.js b/Gruntfile.js
index ad493342436b6..82ae5ff092966 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1346,7 +1346,19 @@ module.exports = function(grunt) {
'uglify:jqueryform'
] );
+ grunt.registerTask( 'build:gutenberg', 'Builds the @wordpress packages in gb-src.', function() {
+ var result = spawn( 'node', [ 'tools/gutenberg/build-packages.js' ], {
+ cwd: __dirname,
+ stdio: 'inherit'
+ } );
+
+ if ( result.status !== 0 ) {
+ grunt.fail.fatal( 'Building the gb-src packages failed.' );
+ }
+ } );
+
grunt.registerTask( 'build:js', [
+ 'build:gutenberg',
'clean:js',
'webpack:prod',
'webpack:dev',
diff --git a/gb-src/.browserslistrc b/gb-src/.browserslistrc
new file mode 100644
index 0000000000000..0152f61ef03df
--- /dev/null
+++ b/gb-src/.browserslistrc
@@ -0,0 +1 @@
+extends @wordpress/browserslist-config
diff --git a/gb-src/.editorconfig b/gb-src/.editorconfig
new file mode 100644
index 0000000000000..a541e47e767bc
--- /dev/null
+++ b/gb-src/.editorconfig
@@ -0,0 +1,21 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+# WordPress Coding Standards
+# https://make.wordpress.org/core/handbook/coding-standards/
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = tab
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/gb-src/.eslintignore b/gb-src/.eslintignore
new file mode 100644
index 0000000000000..aab87ed140ccd
--- /dev/null
+++ b/gb-src/.eslintignore
@@ -0,0 +1,11 @@
+.cache
+build
+build-module
+node_modules
+packages/block-serialization-spec-parser/parser.js
+packages/e2e-tests/plugins
+playground/dist
+vendor
+wordpress
+
+!.eslintrc.js
diff --git a/gb-src/.eslintrc.js b/gb-src/.eslintrc.js
new file mode 100644
index 0000000000000..7d2e6f3d40bca
--- /dev/null
+++ b/gb-src/.eslintrc.js
@@ -0,0 +1,142 @@
+/**
+ * External dependencies
+ */
+const { escapeRegExp } = require( 'lodash' );
+
+/**
+ * Internal dependencies
+ */
+const { version } = require( './package' );
+
+/**
+ * Regular expression string matching a SemVer string with equal major/minor to
+ * the current package version. Used in identifying deprecations.
+ *
+ * @type {string}
+ */
+const majorMinorRegExp = escapeRegExp( version.replace( /\.\d+$/, '' ) ) + '(\\.\\d+)?';
+
+module.exports = {
+ root: true,
+ extends: [
+ 'plugin:@wordpress/eslint-plugin/recommended',
+ 'plugin:eslint-comments/recommended',
+ ],
+ plugins: [
+ 'import',
+ ],
+ globals: {
+ wp: 'off',
+ },
+ rules: {
+ '@wordpress/dependency-group': 'error',
+ '@wordpress/gutenberg-phase': 'error',
+ '@wordpress/react-no-unsafe-timeout': 'error',
+ 'no-restricted-syntax': [
+ 'error',
+ // NOTE: We can't include the forward slash in our regex or
+ // we'll get a `SyntaxError` (Invalid regular expression: \ at end of pattern)
+ // here. That's why we use \\u002F in the regexes below.
+ {
+ selector: 'ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]',
+ message: 'Path access on WordPress dependencies is not allowed.',
+ },
+ {
+ selector: 'ImportDeclaration[source.value=/^react-spring(?!\\u002Fweb\.cjs)/]',
+ message: 'The react-spring dependency must specify CommonJS bundle: react-spring/web.cjs',
+ },
+ {
+ selector: 'CallExpression[callee.name="deprecated"] Property[key.name="version"][value.value=/' + majorMinorRegExp + '/]',
+ message: 'Deprecated functions must be removed before releasing this version.',
+ },
+ {
+ selector: 'CallExpression[callee.name=/^(__|_n|_nx|_x)$/]:not([arguments.0.type=/^Literal|BinaryExpression$/])',
+ message: 'Translate function arguments must be string literals.',
+ },
+ {
+ selector: 'CallExpression[callee.name=/^(_n|_nx|_x)$/]:not([arguments.1.type=/^Literal|BinaryExpression$/])',
+ message: 'Translate function arguments must be string literals.',
+ },
+ {
+ selector: 'CallExpression[callee.name=_nx]:not([arguments.3.type=/^Literal|BinaryExpression$/])',
+ message: 'Translate function arguments must be string literals.',
+ },
+ {
+ selector: 'CallExpression[callee.name=/^(__|_x|_n|_nx)$/] Literal[value=/\\.{3}/]',
+ message: 'Use ellipsis character (…) in place of three dots',
+ },
+ {
+ selector: 'ImportDeclaration[source.value="redux"] Identifier.imported[name="combineReducers"]',
+ message: 'Use `combineReducers` from `@wordpress/data`',
+ },
+ {
+ selector: 'ImportDeclaration[source.value="lodash"] Identifier.imported[name="memoize"]',
+ message: 'Use memize instead of Lodash’s memoize',
+ },
+ {
+ selector: 'CallExpression[callee.object.name="page"][callee.property.name="waitFor"]',
+ message: 'Prefer page.waitForSelector instead.',
+ },
+ {
+ selector: 'JSXAttribute[name.name="id"][value.type="Literal"]',
+ message: 'Do not use string literals for IDs; use withInstanceId instead.',
+ },
+ {
+ // Discourage the usage of `Math.random()` as it's a code smell
+ // for UUID generation, for which we already have a higher-order
+ // component: `withInstanceId`.
+ selector: 'CallExpression[callee.object.name="Math"][callee.property.name="random"]',
+ message: 'Do not use Math.random() to generate unique IDs; use withInstanceId instead. (If you’re not generating unique IDs: ignore this message.)',
+ },
+ {
+ selector: 'CallExpression[callee.name="withDispatch"] > :function > BlockStatement > :not(VariableDeclaration,ReturnStatement)',
+ message: 'withDispatch must return an object with consistent keys. Avoid performing logic in `mapDispatchToProps`.',
+ },
+ {
+ selector: 'LogicalExpression[operator="&&"][left.property.name="length"][right.type="JSXElement"]',
+ message: 'Avoid truthy checks on length property rendering, as zero length is rendered verbatim.',
+ },
+ ],
+ 'react/forbid-elements': [ 'error', {
+ forbid: [
+ [ 'circle', 'Circle' ],
+ [ 'g', 'G' ],
+ [ 'path', 'Path' ],
+ [ 'polygon', 'Polygon' ],
+ [ 'rect', 'Rect' ],
+ [ 'svg', 'SVG' ],
+ ].map( ( [ element, componentName ] ) => {
+ return {
+ element,
+ message: `use cross-platform <${ componentName }> component instead.`,
+ };
+ } ),
+ } ],
+ },
+ overrides: [
+ {
+ files: [ 'packages/**/*.js' ],
+ rules: {
+ 'import/no-extraneous-dependencies': 'error',
+ },
+ excludedFiles: [
+ '**/*.@(android|ios|native).js',
+ '**/@(benchmark|test|__tests__)/**/*.js',
+ ],
+ },
+ {
+ files: [
+ 'packages/jest*/**/*.js',
+ ],
+ extends: [
+ 'plugin:@wordpress/eslint-plugin/test-unit',
+ ],
+ },
+ {
+ files: [ 'packages/e2e-test*/**/*.js' ],
+ extends: [
+ 'plugin:@wordpress/eslint-plugin/test-e2e',
+ ],
+ },
+ ],
+};
diff --git a/gb-src/.github/ISSUE_TEMPLATE/Bug_report.md b/gb-src/.github/ISSUE_TEMPLATE/Bug_report.md
new file mode 100644
index 0000000000000..20aa7016d17e9
--- /dev/null
+++ b/gb-src/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -0,0 +1,36 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+- Please add the version of Gutenberg you are using in the description.
+- To report a security issue, please visit the WordPress HackerOne program: https://hackerone.com/wordpress.
diff --git a/gb-src/.github/ISSUE_TEMPLATE/Custom.md b/gb-src/.github/ISSUE_TEMPLATE/Custom.md
new file mode 100644
index 0000000000000..196cdeb63305f
--- /dev/null
+++ b/gb-src/.github/ISSUE_TEMPLATE/Custom.md
@@ -0,0 +1,17 @@
+---
+name: Help Request
+about: Please post help requests or ‘how to’ questions in support channels first
+
+---
+
+Search first! Your issue may have already been reported.
+
+For general help requests, please post in the support forum at https://wordpress.org/support/forum/how-to-and-troubleshooting/.
+
+Technical help requests have their own section of the support forum at https://wordpress.org/support/forum/wp-advanced/.
+
+You may also ask for technical support at https://wordpress.stackexchange.com/.
+
+Please make sure you have checked the Handbook at https://wordpress.org/gutenberg/handbook before asking your question.
+
+Thank you!
diff --git a/gb-src/.github/ISSUE_TEMPLATE/Feature_request.md b/gb-src/.github/ISSUE_TEMPLATE/Feature_request.md
new file mode 100644
index 0000000000000..7f4075fd55fc5
--- /dev/null
+++ b/gb-src/.github/ISSUE_TEMPLATE/Feature_request.md
@@ -0,0 +1,14 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
diff --git a/gb-src/.github/PULL_REQUEST_TEMPLATE.md b/gb-src/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000000..bef89730704cb
--- /dev/null
+++ b/gb-src/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,22 @@
+## Description
+
+
+## How has this been tested?
+
+
+
+
+## Screenshots
+
+## Types of changes
+
+
+
+
+
+## Checklist:
+- [ ] My code is tested.
+- [ ] My code follows the WordPress code style.
+- [ ] My code follows the accessibility standards.
+- [ ] My code has proper inline documentation.
+- [ ] I've included developer documentation if appropriate.
diff --git a/gb-src/.github/workflows/pull-request-automation.yml b/gb-src/.github/workflows/pull-request-automation.yml
new file mode 100644
index 0000000000000..10277d5a3b45e
--- /dev/null
+++ b/gb-src/.github/workflows/pull-request-automation.yml
@@ -0,0 +1,14 @@
+on: pull_request
+name: Pull request automation
+
+jobs:
+ pull-request-automation:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ # Changing into the action's directory and running `npm install` is much
+ # faster than a full project-wide `npm ci`.
+ - run: cd packages/project-management-automation && npm install
+ - uses: ./packages/project-management-automation
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/gb-src/.jshintignore b/gb-src/.jshintignore
new file mode 100644
index 0000000000000..2cb7d2a2e90e8
--- /dev/null
+++ b/gb-src/.jshintignore
@@ -0,0 +1 @@
+**/*.js
diff --git a/gb-src/.npmrc b/gb-src/.npmrc
new file mode 100644
index 0000000000000..1dab4ed4c3020
--- /dev/null
+++ b/gb-src/.npmrc
@@ -0,0 +1 @@
+save-exact = true
diff --git a/gb-src/.nvmrc b/gb-src/.nvmrc
new file mode 100644
index 0000000000000..f599e28b8ab0d
--- /dev/null
+++ b/gb-src/.nvmrc
@@ -0,0 +1 @@
+10
diff --git a/gb-src/.stylelintrc.json b/gb-src/.stylelintrc.json
new file mode 100644
index 0000000000000..648e4d1113e4a
--- /dev/null
+++ b/gb-src/.stylelintrc.json
@@ -0,0 +1,16 @@
+{
+ "extends": "stylelint-config-wordpress",
+ "rules": {
+ "at-rule-empty-line-before": null,
+ "at-rule-no-unknown": null,
+ "comment-empty-line-before": null,
+ "declaration-property-unit-whitelist": null,
+ "font-weight-notation": null,
+ "max-line-length": null,
+ "no-descending-specificity": null,
+ "no-duplicate-selectors": null,
+ "rule-empty-line-before": null,
+ "selector-class-pattern": null,
+ "value-keyword-case": null
+ }
+}
diff --git a/gb-src/.travis.yml b/gb-src/.travis.yml
new file mode 100644
index 0000000000000..563357347acd0
--- /dev/null
+++ b/gb-src/.travis.yml
@@ -0,0 +1,212 @@
+dist: trusty
+
+language: generic
+
+services:
+ - docker
+
+notifications:
+ email:
+ on_success: never
+ on_failure: change
+
+cache:
+ directories:
+ - $HOME/.composer/cache
+ - $HOME/.jest-cache
+ - $HOME/.npm
+ - $HOME/.nvm/.cache
+
+branches:
+ only:
+ - master
+ - rnmobile/master
+ - /wp\/.*/
+
+env:
+ global:
+ - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
+ - WP_DEVELOP_DIR: ./wordpress
+ - LOCAL_SCRIPT_DEBUG: false
+ - INSTALL_COMPOSER: false
+ - INSTALL_WORDPRESS: true
+
+before_install:
+ - nvm install --latest-npm
+ - |
+ if [[ "$INSTALL_WORDPRESS" = "true" ]]; then
+ # Upgrade docker-compose.
+ sudo rm /usr/local/bin/docker-compose
+ curl -sL https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` > docker-compose
+ chmod +x docker-compose
+ sudo mv docker-compose /usr/local/bin
+ fi
+
+install:
+ # Build Gutenberg.
+ - npm ci
+ - npm run build
+ - |
+ if [[ "$INSTALL_WORDPRESS" = "true" ]]; then
+ # Download and unpack WordPress.
+ curl -sL https://wordpress.org/nightly-builds/wordpress-latest.zip -o /tmp/wordpress-latest.zip
+ unzip -q /tmp/wordpress-latest.zip -d /tmp
+ mkdir -p wordpress/src
+ mv /tmp/wordpress/* wordpress/src
+
+ # Create the upload directory with permissions that Travis can handle.
+ mkdir -p wordpress/src/wp-content/uploads
+ chmod 767 wordpress/src/wp-content/uploads
+
+ # Grab the tools we need for WordPress' local-env.
+ curl -sL https://github.com/WordPress/wordpress-develop/archive/master.zip -o /tmp/wordpress-develop.zip
+ unzip -q /tmp/wordpress-develop.zip -d /tmp
+ mv \
+ /tmp/wordpress-develop-master/tools \
+ /tmp/wordpress-develop-master/tests \
+ /tmp/wordpress-develop-master/.env \
+ /tmp/wordpress-develop-master/docker-compose.yml \
+ /tmp/wordpress-develop-master/wp-cli.yml \
+ /tmp/wordpress-develop-master/*config-sample.php \
+ /tmp/wordpress-develop-master/package.json wordpress
+
+ # Install WordPress.
+ cd wordpress
+ npm install dotenv wait-on
+ npm run env:start
+ sleep 10
+ npm run env:install
+ cd ..
+
+ # Connect Gutenberg to WordPress.
+ npm run env connect
+ npm run env cli plugin activate gutenberg
+ fi
+ - |
+ if [[ "$INSTALL_COMPOSER" = "true" ]]; then
+ npm run env docker-run -- php composer install
+ fi
+ - |
+ if [[ "$E2E_ROLE" = "author" ]]; then
+ npm run env cli -- user create author author@example.com --role=author --user_pass=authpass
+ npm run env cli -- post update 1 --post_author=2
+ fi
+
+jobs:
+ include:
+ - name: Lint
+ install:
+ - npm ci
+ script:
+ - npm run lint
+
+ - name: Build artifacts
+ install:
+ # A "full" install is executed, since `npm ci` does not always exit
+ # with an error status code if the lock file is inaccurate.
+ #
+ # See: https://github.com/WordPress/gutenberg/issues/16157
+ - npm install
+ script:
+ - npm run check-local-changes
+
+ - name: License compatibility
+ install:
+ - npm ci
+ script:
+ - npm run check-licenses
+
+ - name: JavaScript unit tests
+ env: INSTALL_WORDPRESS=false
+ install:
+ - npm ci
+ # It's not necessary to run the full build, since Jest can interpret
+ # source files with `babel-jest`. Some packages have their own custom
+ # build tasks, however. These must be run.
+ - npx lerna run build
+ script:
+ - npm run test-unit -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache"
+
+ - name: JavaScript native mobile tests
+ install:
+ - npm ci
+ # It's not necessary to run the full build, since Jest can interpret
+ # source files with `babel-jest`. Some packages have their own custom
+ # build tasks, however. These must be run.
+ - npx lerna run build
+ script:
+ - npm run test-unit:native -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache"
+
+ - name: PHP unit tests
+ env: INSTALL_COMPOSER=true
+ script:
+ - npm run test-php && npm run test-unit-php-multisite
+
+ - name: PHP unit tests (PHP 5.6)
+ env: INSTALL_COMPOSER=true LOCAL_PHP=5.6-fpm
+ script:
+ - npm run test-php && npm run test-unit-php-multisite
+
+ - name: E2E tests (Admin) (1/4)
+ env: FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 0' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Admin) (2/4)
+ env: FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 1' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Admin) (3/4)
+ env: FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 2' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Admin) (4/4)
+ env: FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 3' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Author) (1/4)
+ env: E2E_ROLE=author FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 0' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Author) (2/4)
+ env: E2E_ROLE=author FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 1' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Author) (3/4)
+ env: E2E_ROLE=author FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 2' < ~/.jest-e2e-tests )
+
+ - name: E2E tests (Author) (4/4)
+ env: E2E_ROLE=author FORCE_REDUCED_MOTION=true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=
+ script:
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests
+ - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 3' < ~/.jest-e2e-tests )
+
+ allow_failures:
+ - name: JavaScript native mobile tests
+
+before_deploy:
+ - npm install
+ - npm run playground:build -- --public-url '/gutenberg'
+
+deploy:
+ provider: pages
+ skip_cleanup: true
+ github_token: $GITHUB_TOKEN
+ keep_history: true
+ local_dir: playground/dist
+ on:
+ branch: master
diff --git a/gb-src/CODE_OF_CONDUCT.md b/gb-src/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000000..5a77323cc3c69
--- /dev/null
+++ b/gb-src/CODE_OF_CONDUCT.md
@@ -0,0 +1,11 @@
+This project comes under the WordPress [Etiquette](https://wordpress.org/about/etiquette/):
+
+In the WordPress open source project, we realize that our biggest asset is the community that we foster. The project, as a whole, follows these basic philosophical principles from The Cathedral and The Bazaar.
+
+- Contributions to the WordPress open source project are for the benefit of the WordPress community as a whole, not specific businesses or individuals. All actions taken as a contributor should be made with the best interests of the community in mind.
+- Participation in the WordPress open source project is open to all who wish to join, regardless of ability, skill, financial status, or any other criteria.
+- The WordPress open source project is a volunteer-run community. Even in cases where contributors are sponsored by companies, that time is donated for the benefit of the entire open source community.
+- Any member of the community can donate their time and contribute to the project in any form including design, code, documentation, community building, etc. For more information, go to make.wordpress.org.
+- The WordPress open source community cares about diversity. We strive to maintain a welcoming environment where everyone can feel included, by keeping communication free of discrimination, incitement to violence, promotion of hate, and unwelcoming behavior.
+
+The team involved will block any user who causes any breach in this.
diff --git a/gb-src/CONTRIBUTING.md b/gb-src/CONTRIBUTING.md
new file mode 100644
index 0000000000000..8ec7046c01f84
--- /dev/null
+++ b/gb-src/CONTRIBUTING.md
@@ -0,0 +1,39 @@
+# Contributing
+
+Thank you for thinking about contributing to WordPress' Gutenberg project! If you're unsure of anything, know that you're 💯 welcome to submit an issue or pull request on any topic. The worst that can happen is that you'll be politely directed to the best location to ask your question or to change something in your pull request. We appreciate any sort of contribution and don't want a wall of rules to get in the way of that.
+
+As with all WordPress projects, we want to ensure a welcoming environment for everyone. With that in mind, all contributors are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md).
+
+Before contributing, we encourage you to review the [Contributor Handbook](https://developer.wordpress.org/block-editor/contributors/). If you have any questions, please ask, either in Slack or open an issue in GitHub so we can help clarify.
+
+All WordPress projects are [licensed under the GPLv2+](/LICENSE.md), and all contributions to Gutenberg will be released under the GPLv2+ license. You maintain copyright over any contribution you make, and by submitting a pull request, you are agreeing to release that contribution under the GPLv2+ license.
+
+This document covers the technical details around setup, and submitting your contribution to the Gutenberg project.
+
+## Developer Contributions
+
+Please see the [Developer Contributions section](/docs/contributors/develop.md) of the Contributor Handbook.
+
+## How Can Designers Contribute?
+
+If you'd like to contribute to the design or front-end, feel free to contribute to tickets labelled [Needs Design](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+label%3A%22Needs+Design%22) or [Needs Design Feedback](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+label%3A"Needs+Design+Feedback%22). We could use your thoughtful replies, mockups, animatics, sketches, doodles. Proposed changes are best done as minimal and specific iterations on the work that precedes it so we can compare. The [WordPress Design team](http://make.wordpress.org/design/) uses [Figma](https://www.figma.com/) to collaborate and share work. If you'd like to contribute, join the [#design channel](http://wordpress.slack.com/messages/design/) in [Slack](https://make.wordpress.org/chat/) and ask the team to set you up with a free Figma account. This will give you access to a helpful [library of components](https://www.figma.com/file/ZtN5xslEVYgzU7Dd5CxgGZwq/WordPress-Components?node-id=0%3A1) used in WordPress.
+
+## Triage Contributions
+
+*Triage* is the practice of reviewing existing issues to make sure they’re relevant, actionable, and have all the information needed to reproduce and/or solve the issue. Triaging is a very important contribution because it allows the community to focus on and prioritise issues, feature proposals, discussions, and so on.
+
+If you want to learn more about triage, and why it it important, please see the [repository management section](docs/contributors/repository-management.md#triaging-issues) of the Contributor Handbook.
+
+## Contribute to the Documentation
+
+Please see the [Documentation section](/docs/contributors/document.md) of the Contributor Handbook.
+
+Documentation is automatically synced from `master` to the [Block Editor Handbook](https://developer.wordpress.org/block-editor/) every 15 minutes.
+
+### `@wordpress/component`
+
+If you're contributing to the documentation of any component from the `@wordpress/component` package, take a look at its [guidelines for contributing](/packages/components/CONTRIBUTING.md).
+
+## Reporting Security Issues
+
+Please see [SECURITY.md](/SECURITY.md).
diff --git a/gb-src/CONTRIBUTORS.md b/gb-src/CONTRIBUTORS.md
new file mode 100644
index 0000000000000..55b2954142873
--- /dev/null
+++ b/gb-src/CONTRIBUTORS.md
@@ -0,0 +1,144 @@
+# Contributors
+
+Gutenberg is built by many contributors and volunteers. Thanks to all of them for their work!
+
+This list is manually curated to include valuable contributions by volunteers that do not include code, such as user testing, providing feedback, or mockups. Please edit this list to include new contributors as they come in. There is no particular order to this list. If you or someone else was omitted from this list, we assure you that was not intentional. Please let us know and we'll add you. For volunteers who contributed their translations, your names are listed on [WordPress Translate site here](https://translate.wordpress.org/projects/wp-plugins/gutenberg/contributors).
+
+| GitHub Username | WordPress.org Username|
+| --------------- | --------------------- |
+| @youknowriad | @youknowriad |
+| @aduth | @aduth |
+| @jasmussen | @joen |
+| @iseulde | @iseulde |
+| @mtias | @mtias |
+| @nylen | @jnylen0 |
+| @EphoxJames | |
+| @mkaz | @mkaz |
+| @notnownikki | @notnownikki |
+| @BE-Webdesign | @chopinbach |
+| @njpanderson | |
+| @mimo84 | |
+| @intronic | |
+| @westonruter | @westonruter |
+| @mcsf | @mcsf |
+| @dmsnell | @dmsnell |
+| @afercia | @afercia |
+| @paulwilde | @paulwilde |
+| @mitogh | @mitogh |
+| @codebykat | @codebykat |
+| @ahmadawais | @mrahmadawais |
+| @kopepasah | @kopepasah |
+| @circlecube | @circlecube |
+| @adamsilverstein | @adamsilverstein |
+| @timmyc | @timmydcrawford |
+| @ephox-mogran | |
+| @nb | @nbachiyski |
+| @JDGrimes | @JDGrimes |
+| @Soean | @Soean |
+| @mapk | @mapk |
+| @sirjonathan | @sirjonathan |
+| @j-falk | @j-falk |
+| @ryelle | @ryelle |
+| @ntwb | @netweb |
+| @lamosty | @lamosty |
+| @willybahuaud | @willybahuaud |
+| @maurobringolf | @maurobringolf |
+| @aaronjorbin | @jorbin |
+| @spocke | @spocke |
+| @androb | @androb |
+| @annaephox | @annaharrison |
+| @Afraithe | |
+| @georgeh | |
+| @m | @matt |
+| @melchoyce | @melchoyce |
+| @pento | @pento |
+| @karmatosed | @karmatosed |
+| @nitrajka | @nitrajka |
+| @sirreal | @jonsurrell |
+| @inhil | |
+| @georgeolaru | @babbardel |
+| @martinlugton | @martinlugton |
+| @joyously | @joyously |
+| @rileybrook | @rileybrook |
+| @azaozz | @azaozz |
+| @folletto | @folletto |
+| @ianstewart | @iandstewart |
+| @johnpixle | @johnpixle |
+| @mrwweb | @mrwweb |
+| @diegoliv | @diegoliv |
+| @lukecav | @lukecavanagh |
+| @shaunandrews | @shaunandrews |
+| @hugobaeta | @hugobaeta |
+| @mizejewski | @mizejewski |
+| @buzztone | @buzztone |
+| @mathetos | @webdevmattcrom |
+| @GaryJones | @garyj |
+| @jasonagnew | |
+| @brickbones | @ieatwebsites |
+| @iamgabrielma | @gma992 |
+| @swissspidy | @swissspidy |
+| @dixitadusara | |
+| @ameeker | @ameeker |
+| @StaggerLeee | @stagger-lee |
+| @jblz | @jblz |
+| @nic-bertino | @nicbertino |
+| @rahmon | @rahmohn |
+| @vladanost | |
+| @gziolo | @gziolo |
+| @lancewillett | @lancewillett |
+| @lynneux | @lynneux |
+| @betsela | @betsela |
+| @fuyuko | @fuyuko |
+| | @msdesign21 |
+| @thrijith | @thrijith |
+| @Cloud887 | |
+| @hblackett | @hblackett |
+| @vishalkakadiya | @vishalkakadiya |
+| @c-shultz | |
+| @nfmohit-wpmudev | @nfmohit |
+| @noisysocks | @noisysocks |
+| @omarreiss | @omarreiss |
+| @hedgefield | @hedgefield |
+| @hideokamoto | @hideokamoto |
+| @mirucon | @mirucon |
+| @nosolosw | @nosolosw |
+| @DannyCooper | @DannyCooper |
+| @burhandodhy | @burhandodhy |
+| @ZebulanStanphill | @zebulan |
+| @BenjaminZekavica | @benjamin_zekavica |
+| @danielbachhuber | @danielbachhuber |
+| @jorgefilipecosta | @jorgefilipecosta |
+| @ajitbohra | @ajitbohra |
+| @ChrisVanPatten | @chrisvanpatten |
+| @mayukojpn | @mayukojpn |
+| @tofumatt | @lonelyvegan |
+| @LukePettway | @luke_pettway |
+| @pratikthink | @pratikthink |
+| @amdrew | @sumobi |
+| @MaedahBatool | @MaedahBatool |
+| @luehrsen | @luehrsen |
+| @getsource | @mikeschroder |
+| @greatislander | @greatislander |
+| @sharazghouri | @sharaz |
+| @jakeparis | @jakeparis |
+| @designsimply | @designsimply |
+| @aldavigdis | @aldavigdis |
+| @miya0001 | @miyauchi |
+| @naogify | @naoki0h |
+| @gutendev | @gutendev |
+| @drdogbot7 | @drdogbot7 |
+| @m-e-h | @m-e-h |
+| @melchoyce | @melchoyce |
+| @sarahmonster | @tinkerbelly |
+| @kjellr | @kjellr |
+| @etoledom | |
+| @hypest | |
+| @koke | |
+| @pinarol | |
+| @daniloercoli | |
+| @marecar3 | |
+| @Tug | |
+| @diegoreymendez | |
+| @mkevins | |
+| @SergioEstevao | |
+| @mzorz | @mzorz |
diff --git a/gb-src/LICENSE.md b/gb-src/LICENSE.md
new file mode 100644
index 0000000000000..7918e383b331d
--- /dev/null
+++ b/gb-src/LICENSE.md
@@ -0,0 +1,400 @@
+### WordPress - Web publishing software
+
+ Copyright 2011-2019 by the contributors
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+This program incorporates work covered by the following copyright and
+permission notices:
+
+ b2 is (c) 2001, 2002 Michel Valdrighi - m@tidakada.com -
+ http://tidakada.com
+
+ Wherever third party code has been used, credit has been given in the code's
+ comments.
+
+ b2 is released under the GPL
+
+and
+
+ WordPress - Web publishing software
+
+ Copyright 2003-2010 by the contributors
+
+ WordPress is released under the GPL
+
+---
+
+### GNU GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+### Preamble
+
+The licenses for most software are designed to take away your freedom
+to share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on,
+we want its recipients to know that what they have is not the
+original, so that any problems introduced by others will not reflect
+on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at
+all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+**0.** This License applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work
+based on the Program" means either the Program or any derivative work
+under copyright law: that is to say, a work containing the Program or
+a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is
+included without limitation in the term "modification".) Each licensee
+is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the Program
+(independent of having been made by running the Program). Whether that
+is true depends on what the Program does.
+
+**1.** You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a
+fee.
+
+**2.** You may modify your copy or copies of the Program or any
+portion of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+**a)** You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+
+**b)** You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any part
+thereof, to be licensed as a whole at no charge to all third parties
+under the terms of this License.
+
+
+**c)** If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such interactive
+use in the most ordinary way, to print or display an announcement
+including an appropriate copyright notice and a notice that there is
+no warranty (or else, saying that you provide a warranty) and that
+users may redistribute the program under these conditions, and telling
+the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an
+announcement, your work based on the Program is not required to print
+an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+**3.** You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+**a)** Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections 1
+and 2 above on a medium customarily used for software interchange; or,
+
+
+**b)** Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your cost of
+physically performing source distribution, a complete machine-readable
+copy of the corresponding source code, to be distributed under the
+terms of Sections 1 and 2 above on a medium customarily used for
+software interchange; or,
+
+
+**c)** Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is allowed
+only for noncommercial distribution and only if you received the
+program in object code or executable form with such an offer, in
+accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+**4.** You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt otherwise
+to copy, modify, sublicense or distribute the Program is void, and
+will automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+**5.** You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+**6.** Each time you redistribute the Program (or any work based on
+the Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+**7.** If, as a consequence of a court judgment or allegation of
+patent infringement or for any other reason (not limited to patent
+issues), conditions are imposed on you (whether by court order,
+agreement or otherwise) that contradict the conditions of this
+License, they do not excuse you from the conditions of this License.
+If you cannot distribute so as to satisfy simultaneously your
+obligations under this License and any other pertinent obligations,
+then as a consequence you may not distribute the Program at all. For
+example, if a patent license would not permit royalty-free
+redistribution of the Program by all those who receive copies directly
+or indirectly through you, then the only way you could satisfy both it
+and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+**8.** If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+**9.** The Free Software Foundation may publish revised and/or new
+versions of the General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Program does not specify a
+version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+**10.** If you wish to incorporate parts of the Program into other
+free programs whose distribution conditions are different, write to
+the author to ask for permission. For software which is copyrighted by
+the Free Software Foundation, write to the Free Software Foundation;
+we sometimes make exceptions for this. Our decision will be guided by
+the two goals of preserving the free status of all derivatives of our
+free software and of promoting the sharing and reuse of software
+generally.
+
+**NO WARRANTY**
+
+**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+### END OF TERMS AND CONDITIONS
+
+### How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ one line to give the program's name and an idea of what it does.
+ Copyright (C) yyyy name of author
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Also add information on how to contact you by electronic and paper
+mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+ type `show w'. This is free software, and you are welcome
+ to redistribute it under certain conditions; type `show c'
+ for details.
+
+The hypothetical commands \`show w' and \`show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than \`show w' and
+\`show c'; they could even be mouse-clicks or menu items--whatever
+suits your program.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the program,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright
+ interest in the program `Gnomovision'
+ (which makes passes at compilers) written
+ by James Hacker.
+
+ signature of Ty Coon, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library,
+you may consider it more useful to permit linking proprietary
+applications with the library. If this is what you want to do, use the
+[GNU Lesser General Public
+License](http://www.gnu.org/licenses/lgpl.html) instead of this
+License.
diff --git a/gb-src/README.md b/gb-src/README.md
new file mode 100644
index 0000000000000..494b9ba91e7be
--- /dev/null
+++ b/gb-src/README.md
@@ -0,0 +1,79 @@
+# Gutenberg
+[](https://travis-ci.com/WordPress/gutenberg)
+[](https://lernajs.io/)
+
+
+
+This repo is the development hub for the editor focus in WordPress Core. `Gutenberg` is the project name.
+
+## Getting started
+- **Download:** If you want to use the latest release with your WordPress site, download the latest release from the WordPress.org plugins repository.
+- **Discuss:** Conversations and discussions take place in `#core-editor` channel on the Making WordPress Slack.
+- **Contribute:** Development of Gutenberg happens in this GitHub repo. Get started by reading the contributing guidelines.
+- **Learn:** Discover more about the project on WordPress.org.
+
+**Gutenberg is more than an editor.** While the project is currently focused on building the new editor for WordPress, it doesn't end there. This lays the groundwork for a new model for WordPress Core that will ultimately impact the entire publishing experience of the platform.
+
+## Editing focus
+
+> *The editor will create a new page- and post-building experience that makes writing rich posts effortless, and has “blocks” to make it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.*
+>
+> — Matt Mullenweg
+
+One thing that sets WordPress apart is that it allows you to create a post layout that's as rich as you can imagine—but only if you can build your own custom theme with HTML and CSS. By thinking of the editor as a tool that allows you to write rich posts **and** create beautiful layouts, we can transform WordPress into something users _love_, as opposed to something they choose because it happens to be what everyone else uses.
+
+**Gutenberg is a new way forward.** It looks at the editor as more than a content field, revisiting a layout that has been largely unchanged for almost a decade. This project allows The WordPress Project to holistically design a modern editing experience and build a foundation for things to come.
+
+Here's why we're looking at the whole editing screen, as opposed to just the content field:
+
+1. **The block unifies multiple interfaces.** If Gutenberg added blocks on top of the existing interface, it would _add_ complexity, as opposed to removing it.
+2. **Simplified (and enhanced) editing.** By revisiting the interface, Gutenberg can modernize the writing, editing, and publishing experience, with usability and simplicity in mind, benefitting both new and casual users.
+3. **Better interface usability.** When singular block interface takes center stage, it demonstrates a clear path forward for developers to create premium blocks, superior to both shortcodes and widgets.
+4. **A fresh look at content creation.** Considering the whole interface lays a solid foundation for the next focus: full site customization.
+5. **Modern tooling.** Looking at the full editor screen also gives WordPress the opportunity to drastically modernize the foundation, and take steps towards a more fluid and JavaScript-powered future that fully leverages the WordPress REST API.
+
+
+
+## Blocks
+
+Blocks are the unifying evolution of what is now covered, in different ways, by shortcodes, embeds, widgets, post formats, custom post types, theme options, meta-boxes, and other formatting elements. They embrace the breadth of functionality WordPress is capable of, with the clarity of a consistent user experience.
+
+Imagine a custom `employee` block that a client can drag onto an `About` page to automatically display a picture, name, and bio of all the employees. Imagine a whole universe of plugins just as flexible, all extending WordPress in the same way. Imagine simplified menus and widgets. Users who can instantly understand and use WordPress—and 90% of plugins. This will allow you to easily compose beautiful posts like this example.
+
+Check out the FAQ for answers to the most common questions about the project.
+
+## Compatibility
+
+Posts are backward compatible, and shortcodes will still work. We are continuously exploring how highly-tailored meta boxes can be accommodated, and are looking at solutions ranging from a plugin to disable Gutenberg to automatically detecting whether to load Gutenberg or not. While we want to make sure the new editing experience from writing to publishing is user-friendly, we’re committed to finding a good solution for highly-tailored existing sites.
+
+## The stages of Gutenberg
+
+Gutenberg has three planned stages.
+1) **The first, aimed for inclusion in WordPress 5.0, focuses on the post editing experience** and the implementation of blocks. This initial phase focuses on a content-first approach. The use of blocks, as detailed above, allows you to focus on how your content will look without the distraction of other configuration options. This ultimately will help all users present their content in a way that is engaging, direct, and visual. These foundational elements will pave the way forward.
+2) Planned for 2019, **The second stage focuses on overhauling The Customizer** and page templates.
+3) Ultimately, **full site customization** will be possible.
+
+**Gutenberg is a big change.** There will be ways to ensure that existing functionality (like shortcodes and meta-boxes) continue to work while allowing developers the time and paths to transition effectively. Ultimately, it will open new opportunities for plugin and theme developers to better serve users through a more engaging and visual experience that takes advantage of a toolset supported by core.
+
+## Get involved
+
+We’re calling this editor project "Gutenberg" because it's a big undertaking. We are working on it every day in GitHub, and we'd love your help building it. You’re also welcome to give feedback, the easiest is to join us in our Slack channel, `#core-editor`. A weekly meeting is held in the Slack channel on Wednesdays at 13:00 UTC.
+
+## Contributors
+
+Gutenberg is built by many contributors and volunteers. Please see the full list in CONTRIBUTORS.md.
+
+## How You Can Contribute
+
+Please see CONTRIBUTING.md.
+
+## Further Reading
+
+- Gutenberg, or the Ship of Theseus, with examples of what Gutenberg might do in the future
+- Editor Technical Overview
+- Design Principles and block design best practices
+- WP Post Grammar Parser
+- Development updates on make.wordpress.org
+- Documentation: Creating Blocks, Reference, and Guidelines
+
+
diff --git a/gb-src/SECURITY.md b/gb-src/SECURITY.md
new file mode 100644
index 0000000000000..ed78e7eeb2cde
--- /dev/null
+++ b/gb-src/SECURITY.md
@@ -0,0 +1,5 @@
+# Reporting Security Issues
+
+The Gutenberg team and WordPress community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
+
+To report a security issue, please visit the [WordPress HackerOne](https://hackerone.com/wordpress) program.
diff --git a/gb-src/assets/stylesheets/_animations.scss b/gb-src/assets/stylesheets/_animations.scss
new file mode 100644
index 0000000000000..b5e6655e660cb
--- /dev/null
+++ b/gb-src/assets/stylesheets/_animations.scss
@@ -0,0 +1,5 @@
+@mixin edit-post__fade-in-animation($speed: 0.2s, $delay: 0s) {
+ animation: edit-post__fade-in-animation $speed ease-out $delay;
+ animation-fill-mode: forwards;
+ @include reduce-motion("animation");
+}
diff --git a/gb-src/assets/stylesheets/_breakpoints.scss b/gb-src/assets/stylesheets/_breakpoints.scss
new file mode 100644
index 0000000000000..3dd164c99753a
--- /dev/null
+++ b/gb-src/assets/stylesheets/_breakpoints.scss
@@ -0,0 +1,41 @@
+/**
+ * Breakpoints & Media Queries
+ */
+
+// Most used breakpoints
+$break-huge: 1440px;
+$break-wide: 1280px;
+$break-xlarge: 1080px;
+$break-large: 960px; // admin sidebar auto folds
+$break-medium: 782px; // adminbar goes big
+$break-small: 600px;
+$break-mobile: 480px;
+$break-zoomed-in: 280px;
+
+// All media queries currently in WordPress:
+//
+// min-width: 2000px
+// min-width: 1680px
+// min-width: 1250px
+// max-width: 1120px *
+// max-width: 1000px
+// min-width: 769px and max-width: 1000px
+// max-width: 960px *
+// max-width: 900px
+// max-width: 850px
+// min-width: 800px and max-width: 1499px
+// max-width: 800px
+// max-width: 799px
+// max-width: 782px *
+// max-width: 768px
+// max-width: 640px *
+// max-width: 600px *
+// max-width: 520px
+// max-width: 500px
+// max-width: 480px *
+// max-width: 400px *
+// max-width: 380px
+// max-width: 320px *
+//
+// Those marked * seem to be more commonly used than the others.
+// Let's try and use as few of these as possible, and be mindful about adding new ones, so we don't make the situation worse
diff --git a/gb-src/assets/stylesheets/_colors.scss b/gb-src/assets/stylesheets/_colors.scss
new file mode 100644
index 0000000000000..cafa84fba44ca
--- /dev/null
+++ b/gb-src/assets/stylesheets/_colors.scss
@@ -0,0 +1,90 @@
+/**
+ * Colors
+ */
+
+// Hugo's new WordPress shades of gray, from http://codepen.io/hugobaeta/pen/grJjVp.
+$black: #000;
+$dark-gray-900: #191e23;
+$dark-gray-800: #23282d;
+$dark-gray-700: #32373c;
+$dark-gray-600: #40464d;
+$dark-gray-500: #555d66; // Use this most of the time for dark items.
+$dark-gray-400: #606a73;
+$dark-gray-300: #6c7781; // Lightest gray that can be used for AA text contrast.
+$dark-gray-200: #7e8993;
+$dark-gray-150: #8d96a0; // Lightest gray that can be used for AA non-text contrast.
+$dark-gray-100: #8f98a1;
+$light-gray-900: #a2aab2;
+$light-gray-800: #b5bcc2;
+$light-gray-700: #ccd0d4;
+$light-gray-600: #d7dade;
+$light-gray-500: #e2e4e7; // Good for "grayed" items and borders.
+$light-gray-400: #e8eaeb; // Good for "readonly" input fields and special text selection.
+$light-gray-300: #edeff0;
+$light-gray-200: #f3f4f5;
+$light-gray-100: #f8f9f9;
+$white: #fff;
+
+// Dark opacities, for use with light themes.
+$dark-opacity-900: rgba(#000510, 0.9);
+$dark-opacity-800: rgba(#00000a, 0.85);
+$dark-opacity-700: rgba(#06060b, 0.8);
+$dark-opacity-600: rgba(#000913, 0.75);
+$dark-opacity-500: rgba(#0a1829, 0.7);
+$dark-opacity-400: rgba(#0a1829, 0.65);
+$dark-opacity-300: rgba(#0e1c2e, 0.62);
+$dark-opacity-200: rgba(#162435, 0.55);
+$dark-opacity-100: rgba(#223443, 0.5);
+$dark-opacity-light-900: rgba(#304455, 0.45);
+$dark-opacity-light-800: rgba(#425863, 0.4);
+$dark-opacity-light-700: rgba(#667886, 0.35);
+$dark-opacity-light-600: rgba(#7b86a2, 0.3);
+$dark-opacity-light-500: rgba(#9197a2, 0.25);
+$dark-opacity-light-400: rgba(#95959c, 0.2);
+$dark-opacity-light-300: rgba(#829493, 0.15);
+$dark-opacity-light-200: rgba(#8b8b96, 0.1);
+$dark-opacity-light-100: rgba(#747474, 0.05);
+$dark-opacity-background-fill: rgba($dark-gray-700, 0.7); // Similar to $dark-opacity-light-200, but more opaque.
+
+// Light opacities, for use with dark themes.
+$light-opacity-900: rgba($white, 1);
+$light-opacity-800: rgba($white, 0.9);
+$light-opacity-700: rgba($white, 0.85);
+$light-opacity-600: rgba($white, 0.8);
+$light-opacity-500: rgba($white, 0.75);
+$light-opacity-400: rgba($white, 0.7);
+$light-opacity-300: rgba($white, 0.65);
+$light-opacity-200: rgba($white, 0.6);
+$light-opacity-100: rgba($white, 0.55);
+$light-opacity-light-900: rgba($white, 0.5);
+$light-opacity-light-800: rgba($white, 0.45);
+$light-opacity-light-700: rgba($white, 0.4);
+$light-opacity-light-600: rgba($white, 0.35);
+$light-opacity-light-500: rgba($white, 0.3);
+$light-opacity-light-400: rgba($white, 0.25);
+$light-opacity-light-300: rgba($white, 0.2);
+$light-opacity-light-200: rgba($white, 0.15);
+$light-opacity-light-100: rgba($white, 0.1);
+$light-opacity-background-fill: rgba($light-gray-300, 0.8); // Similar to $light-opacity-light-200, but more opaque.
+
+// Additional colors.
+// Some are from https://make.wordpress.org/design/handbook/foundations/colors/.
+$blue-wordpress-700: #00669b;
+$blue-dark-900: #0071a1;
+
+$blue-medium-900: #006589;
+$blue-medium-800: #00739c;
+$blue-medium-700: #007fac;
+$blue-medium-600: #008dbe;
+$blue-medium-500: #00a0d2;
+$blue-medium-400: #33b3db;
+$blue-medium-300: #66c6e4;
+$blue-medium-200: #bfe7f3;
+$blue-medium-100: #e5f5fa;
+$blue-medium-highlight: #b3e7fe;
+$blue-medium-focus: #007cba;
+
+// Alert colors.
+$alert-yellow: #f0b849;
+$alert-red: #d94f4f;
+$alert-green: #4ab866;
diff --git a/gb-src/assets/stylesheets/_mixins.scss b/gb-src/assets/stylesheets/_mixins.scss
new file mode 100644
index 0000000000000..fda485a779275
--- /dev/null
+++ b/gb-src/assets/stylesheets/_mixins.scss
@@ -0,0 +1,612 @@
+/**
+ * Breakpoint mixins
+ */
+
+@mixin break-huge() {
+ @media (min-width: #{ ($break-huge) }) {
+ @content;
+ }
+}
+
+@mixin break-wide() {
+ @media (min-width: #{ ($break-wide) }) {
+ @content;
+ }
+}
+
+@mixin break-xlarge() {
+ @media (min-width: #{ ($break-xlarge) }) {
+ @content;
+ }
+}
+
+@mixin break-large() {
+ @media (min-width: #{ ($break-large) }) {
+ @content;
+ }
+}
+
+@mixin break-medium() {
+ @media (min-width: #{ ($break-medium) }) {
+ @content;
+ }
+}
+
+@mixin break-small() {
+ @media (min-width: #{ ($break-small) }) {
+ @content;
+ }
+}
+
+@mixin break-mobile() {
+ @media (min-width: #{ ($break-mobile) }) {
+ @content;
+ }
+}
+
+@mixin break-zoomed-in() {
+ @media (min-width: #{ ($break-zoomed-in) }) {
+ @content;
+ }
+}
+
+
+/**
+ * Long content fade mixin
+ *
+ * Creates a fading overlay to signify that the content is longer
+ * than the space allows.
+ */
+
+@mixin long-content-fade($direction: right, $size: 20%, $color: #fff, $edge: 0, $z-index: false) {
+ content: "";
+ display: block;
+ position: absolute;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ pointer-events: none;
+
+ @if $z-index {
+ z-index: $z-index;
+ }
+
+ @if $direction == "bottom" {
+ background: linear-gradient(to top, rgba($color, 0), $color 90%);
+ left: $edge;
+ right: $edge;
+ top: $edge;
+ bottom: calc(100% - $size);
+ width: auto;
+ }
+
+ @if $direction == "top" {
+ background: linear-gradient(to bottom, rgba($color, 0), $color 90%);
+ top: calc(100% - $size);
+ left: $edge;
+ right: $edge;
+ bottom: $edge;
+ width: auto;
+ }
+
+ @if $direction == "left" {
+ background: linear-gradient(to left, rgba($color, 0), $color 90%);
+ top: $edge;
+ left: $edge;
+ bottom: $edge;
+ right: auto;
+ width: $size;
+ height: auto;
+ }
+
+ @if $direction == "right" {
+ background: linear-gradient(to right, rgba($color, 0), $color 90%);
+ top: $edge;
+ bottom: $edge;
+ right: $edge;
+ left: auto;
+ width: $size;
+ height: auto;
+ }
+}
+
+/**
+ * Button states and focus styles
+ */
+
+// Buttons with rounded corners.
+@mixin button-style__disabled {
+ opacity: 0.6;
+ cursor: default;
+}
+
+@mixin button-style__hover {
+ background-color: $white;
+ color: $dark-gray-900;
+ box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px $white;
+}
+
+@mixin button-style__active() {
+ outline: none;
+ background-color: $white;
+ color: $dark-gray-900;
+ box-shadow: inset 0 0 0 1px $light-gray-700, inset 0 0 0 2px $white;
+}
+
+@mixin button-style__focus-active() {
+ background-color: $white;
+ color: $dark-gray-900;
+ box-shadow: inset 0 0 0 1px $dark-gray-300, inset 0 0 0 2px $white;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+}
+
+// Switch.
+@mixin switch-style__focus-active() {
+ box-shadow: 0 0 0 2px $white, 0 0 0 3px $dark-gray-300;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+ outline-offset: 2px;
+}
+
+// Formatting Buttons.
+@mixin formatting-button-style__hover {
+ color: $dark-gray-500;
+ box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px $white;
+}
+
+@mixin formatting-button-style__active() {
+ outline: none;
+ color: $white;
+ box-shadow: none;
+ background: $dark-gray-500;
+}
+
+@mixin formatting-button-style__focus() {
+ box-shadow: inset 0 0 0 1px $dark-gray-500, inset 0 0 0 2px $white;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+}
+
+// Tabs, Inputs, Square buttons.
+@mixin input-style__neutral() {
+ box-shadow: 0 0 0 transparent;
+ transition: box-shadow 0.1s linear;
+ border-radius: $radius-round-rectangle;
+ border: $border-width solid $dark-gray-200;
+ @include reduce-motion("transition");
+}
+
+@mixin input-style__focus() {
+ color: $dark-gray-900;
+ border-color: $blue-medium-focus;
+ box-shadow: 0 0 0 1px $blue-medium-focus;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+}
+
+// Square buttons.
+@mixin square-style__neutral() {
+ outline-offset: -1px;
+}
+
+@mixin square-style__focus() {
+ color: $dark-gray-900;
+ outline-offset: -1px;
+ outline: 1px dotted $dark-gray-500;
+}
+
+// Menu items.
+@mixin menu-style__neutral() {
+ border: none;
+ box-shadow: none;
+}
+
+@mixin menu-style__hover() {
+ color: $dark-gray-900;
+ border: none;
+ box-shadow: none;
+ background: $light-gray-200;
+}
+
+@mixin menu-style__focus() {
+ color: $dark-gray-900;
+ border: none;
+ box-shadow: none;
+ outline-offset: -2px;
+ outline: 1px dotted $dark-gray-500;
+}
+
+// Blocks in the Library.
+@mixin block-style__disabled {
+ opacity: 0.6;
+ cursor: default;
+}
+
+@mixin block-style__hover {
+ background: $light-gray-200;
+ color: $dark-gray-900;
+}
+
+@mixin block-style__focus() {
+ color: $dark-gray-900;
+ box-shadow: 0 0 0 1px $white, 0 0 0 3px $blue-medium-500;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+}
+
+@mixin block-style__is-active() {
+ color: $dark-gray-900;
+ box-shadow: inset 0 0 0 2px $dark-gray-500;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 2px solid transparent;
+ outline-offset: -2px;
+}
+
+@mixin block-style__is-active-focus() {
+ color: $dark-gray-900;
+ box-shadow: 0 0 0 1px $white, 0 0 0 3px $blue-medium-500, inset 0 0 0 2px $dark-gray-500;
+
+ // Windows High Contrast mode will show this outline, but not the box-shadow.
+ outline: 4px solid transparent;
+ outline-offset: -4px;
+}
+
+/**
+ * Applies editor left position to the selector passed as argument
+ */
+
+@mixin editor-left($selector) {
+ #{$selector} { /* Set left position when auto-fold is not on the body element. */
+ left: 0;
+
+ @include break-medium() {
+ left: $admin-sidebar-width;
+ }
+ }
+
+ .auto-fold #{$selector} { /* Auto fold is when on smaller breakpoints, nav menu auto collapses. */
+ @include break-medium() {
+ left: $admin-sidebar-width-collapsed;
+ }
+
+ @include break-large() {
+ left: $admin-sidebar-width;
+ }
+ }
+
+ /* Sidebar manually collapsed. */
+ .folded #{$selector} {
+ left: 0;
+
+ @include break-medium() {
+ left: $admin-sidebar-width-collapsed;
+ }
+ }
+
+ /* Mobile menu opened. */
+ @media (max-width: #{ ($break-medium) }) {
+ .auto-fold .wp-responsive-open #{$selector} {
+ left: $admin-sidebar-width-big;
+ }
+ }
+
+ /* In small screens with responsive menu expanded there is small white space. */
+ @media (max-width: #{ ($break-small) }) {
+ .auto-fold .wp-responsive-open #{$selector} {
+ margin-left: -18px;
+ }
+ }
+
+ body.is-fullscreen-mode #{$selector} {
+ left: 0 !important;
+ }
+}
+
+/**
+ * Applies editor right position to the selector passed as argument
+ */
+
+@mixin editor-right($selector) {
+ #{ $selector } {
+ right: 0;
+ }
+
+ .edit-post-layout.is-sidebar-opened #{ $selector } {
+ right: $sidebar-width;
+ }
+}
+
+
+/**
+ * Styles that are reused verbatim in a few places
+ */
+
+// These are additional styles for all captions, when the theme opts in to block styles.
+@mixin caption-style() {
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+}
+
+@mixin caption-style-theme() {
+ color: $dark-gray-500;
+ font-size: $default-font-size;
+ text-align: center;
+}
+
+@mixin dropdown-arrow() {
+ content: "";
+ pointer-events: none;
+ display: block;
+ width: 0;
+ height: 0;
+ border-left: 3px solid transparent;
+ border-right: 3px solid transparent;
+ border-top: 5px solid;
+ margin-left: $grid-size-small;
+
+ // This gives the icon space on the right side consistent with the material
+ // icon standards.
+ margin-right: 2px;
+}
+
+/**
+ * Allows users to opt-out of animations via OS-level preferences.
+ */
+
+@mixin reduce-motion($property: "") {
+
+ @if $property == "transition" {
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0s;
+ }
+ }
+
+ @else if $property == "animation" {
+ @media (prefers-reduced-motion: reduce) {
+ animation-duration: 1ms;
+ }
+ }
+
+ @else {
+ @media (prefers-reduced-motion: reduce) {
+ transition-duration: 0s;
+ animation-duration: 1ms;
+ }
+ }
+
+}
+
+/**
+ * Reset default styles for JavaScript UI based pages.
+ * This is a WP-admin agnostic reset
+ */
+@mixin reset {
+ box-sizing: border-box;
+
+ *,
+ *::before,
+ *::after {
+ box-sizing: inherit;
+ }
+
+ .input-control, // Upstream name is `.regular-text`.
+ input[type="text"],
+ input[type="search"],
+ input[type="radio"],
+ input[type="tel"],
+ input[type="time"],
+ input[type="url"],
+ input[type="week"],
+ input[type="password"],
+ input[type="checkbox"],
+ input[type="color"],
+ input[type="date"],
+ input[type="datetime"],
+ input[type="datetime-local"],
+ input[type="email"],
+ input[type="month"],
+ input[type="number"],
+ select,
+ textarea {
+ font-family: $default-font;
+ padding: 6px 8px;
+ @include input-style__neutral();
+
+ /* Fonts smaller than 16px causes mobile safari to zoom. */
+ font-size: $mobile-text-min-font-size;
+ /* Override core line-height. To be reviewed. */
+ line-height: normal;
+ @include break-small {
+ font-size: $default-font-size;
+ /* Override core line-height. To be reviewed. */
+ line-height: normal;
+ }
+
+ &:focus {
+ @include input-style__focus();
+ }
+ }
+
+ input[type="number"] {
+ padding-left: 4px;
+ padding-right: 4px;
+ }
+
+ select {
+ padding: 2px;
+ font-size: $default-font-size;
+ color: $dark-gray-500;
+
+ &:focus {
+ border-color: $blue-medium-600;
+ // Windows High Contrast mode will show this outline
+ outline: 2px solid transparent;
+ outline-offset: 0;
+ }
+ }
+
+ input[type="checkbox"],
+ input[type="radio"] {
+ border: $border-width + 1 solid $dark-gray-300;
+ margin-right: 12px;
+ transition: none;
+
+ &:focus {
+ border-color: $dark-gray-300;
+ box-shadow: 0 0 0 1px $dark-gray-300;
+ }
+
+ &:checked {
+ background: theme(toggle);
+ border-color: theme(toggle);
+ }
+
+ &:checked:focus {
+ box-shadow: 0 0 0 2px $dark-gray-500;
+ }
+ }
+
+ input[type="checkbox"] {
+ border-radius: $radius-round-rectangle / 2;
+
+ &:checked::before,
+ &[aria-checked="mixed"]::before {
+ margin: -3px -5px;
+ color: $white;
+
+ @include break-medium() {
+ margin: -4px 0 0 -5px;
+ }
+ }
+
+ &[aria-checked="mixed"] {
+ background: theme(toggle);
+ border-color: theme(toggle);
+
+ &::before {
+ // Inherited from `forms.css`.
+ // See: https://github.com/WordPress/wordpress-develop/tree/5.1.1/src/wp-admin/css/forms.css#L122-L132
+ content: "\f460";
+ float: left;
+ display: inline-block;
+ vertical-align: middle;
+ width: 16px;
+ /* stylelint-disable */
+ font: normal 30px/1 dashicons;
+ /* stylelint-enable */
+ speak: none;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+
+ @include break-medium() {
+ float: none;
+ font-size: 21px;
+ }
+ }
+
+ &:focus {
+ box-shadow: 0 0 0 2px $dark-gray-500;
+ }
+ }
+ }
+
+ // We provide explicit pixel dimensions to ensure a crisp appearance.
+ // This radio button style should be ported upstream.
+ input[type="radio"] {
+ border-radius: $radius-round;
+
+ &:checked::before {
+ width: 6px;
+ height: 6px;
+ margin: 6px 0 0 6px;
+ background-color: $white;
+
+ @include break-medium() {
+ margin: 3px 0 0 3px;
+ }
+ }
+ }
+
+ // Placeholder colors
+ input,
+ textarea {
+ // Use opacity to work in various editor styles.
+ &::-webkit-input-placeholder {
+ color: $dark-opacity-300;
+ }
+
+ &::-moz-placeholder {
+ opacity: 1; // Necessary because Firefox reduces this from 1.
+ color: $dark-opacity-300;
+ }
+
+ &:-ms-input-placeholder {
+ color: $dark-opacity-300;
+ }
+
+ .is-dark-theme & {
+ &::-webkit-input-placeholder {
+ color: $light-opacity-300;
+ }
+
+ &::-moz-placeholder {
+ opacity: 1; // Necessary because Firefox reduces this from 1.
+ color: $light-opacity-300;
+ }
+
+ &:-ms-input-placeholder {
+ color: $light-opacity-300;
+ }
+ }
+ }
+}
+
+/**
+ * Reset the WP Admin page styles for Gutenberg-like pages.
+ */
+@mixin wp-admin-reset( $content-container ) {
+ background: $white;
+
+ #wpcontent {
+ padding-left: 0;
+ }
+
+ #wpbody-content {
+ padding-bottom: 0;
+ }
+
+ /* We hide legacy notices in Gutenberg Based Pages, because they were not designed in a way that scaled well.
+ Plugins can use Gutenberg notices if they need to pass on information to the user when they are editing. */
+ #wpbody-content > div:not(#{ $content-container }):not(#screen-meta) {
+ display: none;
+ }
+
+ #wpfooter {
+ display: none;
+ }
+
+ .a11y-speak-region {
+ left: -1px;
+ top: -1px;
+ }
+
+ ul#adminmenu a.wp-has-current-submenu::after,
+ ul#adminmenu > li.current > a.current::after {
+ border-right-color: $white;
+ }
+
+ .media-frame select.attachment-filters:last-of-type {
+ width: auto;
+ max-width: 100%;
+ }
+}
diff --git a/gb-src/assets/stylesheets/_variables.scss b/gb-src/assets/stylesheets/_variables.scss
new file mode 100644
index 0000000000000..652be4700ff22
--- /dev/null
+++ b/gb-src/assets/stylesheets/_variables.scss
@@ -0,0 +1,71 @@
+/**
+ * Often re-used variables
+ */
+
+// Fonts & basics
+$default-font: -apple-system, BlinkMacSystemFont,"Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell,"Helvetica Neue", sans-serif;
+$default-font-size: 13px;
+$default-line-height: 1.4;
+$editor-font: "Noto Serif", serif;
+$editor-html-font: Menlo, Consolas, monaco, monospace;
+$editor-font-size: 16px;
+$default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks (it's 2x $block-padding).
+$text-editor-font-size: 14px;
+$editor-line-height: 1.8;
+$big-font-size: 18px;
+$mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in"
+
+// Grid size
+$grid-size-small: 4px;
+$grid-size: 8px;
+$grid-size-large: 16px;
+$grid-size-xlarge: 24px;
+
+// Widths, heights & dimensions
+$panel-padding: 16px;
+$header-height: 56px;
+$panel-header-height: 50px;
+$admin-bar-height: 32px;
+$admin-bar-height-big: 46px;
+$admin-sidebar-width: 160px;
+$admin-sidebar-width-big: 190px;
+$admin-sidebar-width-collapsed: 36px;
+$empty-paragraph-height: $text-editor-font-size * 4;
+$modal-min-width: 360px;
+
+// Visuals
+$shadow-popover: 0 3px 30px rgba($dark-gray-900, 0.1);
+$shadow-toolbar: 0 2px 10px rgba($dark-gray-900, 0.1), 0 0 2px rgba($dark-gray-900, 0.1);
+$shadow-below-only: 0 5px 10px rgba($dark-gray-900, 0.05), 0 2px 2px rgba($dark-gray-900, 0.05);
+$shadow-modal: 0 3px 30px rgba($dark-gray-900, 0.2);
+
+// Editor Widths
+$sidebar-width: 280px;
+$content-width: 610px; // For the visual width, subtract 30px (2 * $block-padding + 2px borders). This comes to 580px, which is optimized for 70 characters.
+
+// Block UI
+$border-width: 1px;
+$block-controls-height: 36px;
+$icon-button-size: 36px;
+$icon-button-size-small: 24px;
+$inserter-tabs-height: 36px;
+$block-toolbar-height: $block-controls-height + $border-width;
+$resize-handler-size: 15px;
+$resize-handler-container-size: $resize-handler-size + ($grid-size-small * 2); // Make the resize handle container larger so there's a larger grabbable area.
+
+// Blocks
+$block-left-border-width: $border-width * 3;
+$block-padding: 14px; // Space between block footprint and focus boundaries. These are drawn outside the block footprint, and do not affect the size.
+$block-spacing: 4px; // Vertical space between blocks.
+$block-side-ui-width: 28px; // Width of the movers/drag handle UI.
+$block-side-ui-clearance: 2px; // Space between movers/drag handle UI, and block.
+$block-container-side-padding: $block-side-ui-width + $block-padding + 2 * $block-side-ui-clearance; // Total space left and right of the block footprint.
+$block-bg-padding--v: $block-padding + $block-spacing + $block-side-ui-clearance; // padding for Blocks with a background color (eg: paragraph or group)
+$block-bg-padding--h: $block-side-ui-width + $block-side-ui-clearance; // padding for Blocks with a background color (eg: paragraph or group)
+
+// Buttons & UI Widgets
+$radius-round-rectangle: 4px;
+$radius-round: 50%;
+
+// Widgets screen
+$widget-area-width: 700px;
diff --git a/gb-src/assets/stylesheets/_z-index.scss b/gb-src/assets/stylesheets/_z-index.scss
new file mode 100644
index 0000000000000..c93cf93a47254
--- /dev/null
+++ b/gb-src/assets/stylesheets/_z-index.scss
@@ -0,0 +1,125 @@
+// Stores a list of z-index values in a central location. For clarity, when a
+// specific value is needed, add a comment explaining why (what other rules the
+// value is designed to work with).
+
+$z-layers: (
+ ".block-editor-block-list__block-edit::before": 0,
+ ".block-editor-block-switcher__arrow": 1,
+ ".block-editor-block-list__block {core/image aligned wide or fullwide}": 20,
+ ".block-library-classic__toolbar": 10,
+ ".block-editor-block-list__layout .reusable-block-indicator": 1,
+ ".block-editor-block-list__breadcrumb": 22,
+ ".components-form-toggle__input": 1,
+ ".components-panel__header.edit-post-sidebar__panel-tabs": -1,
+ ".edit-post-sidebar .components-panel": -2,
+ ".block-editor-inserter__tabs": 1,
+ ".block-editor-inserter__tab.is-active": 1,
+ ".components-panel__header": 1,
+ ".components-modal__header": 10,
+ ".edit-post-meta-boxes-area.is-loading::before": 1,
+ ".edit-post-meta-boxes-area .spinner": 5,
+ ".components-popover__close": 5,
+ ".block-editor-block-list__insertion-point": 6,
+ ".block-editor-inserter-with-shortcuts": 5,
+ ".block-editor-warning": 5,
+ ".block-library-gallery-item__inline-menu": 20,
+ ".block-editor-url-input__suggestions": 30,
+ ".edit-post-header": 30,
+ ".edit-widgets-header": 30,
+ ".block-library-button__inline-link .block-editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter
+ ".block-library-image__resize-handlers": 1, // Resize handlers above sibling inserter
+ ".wp-block-cover__inner-container": 1, // InnerBlocks area inside cover image block
+ ".wp-block-cover.has-background-dim::before": 1, // Overlay area inside block cover need to be higher than the video background.
+ ".wp-block-cover__video-background": 0, // Video background inside cover block.
+
+ // Active pill button
+ ".components-button.is-button {:focus or .is-primary}": 1,
+
+ // The draggable element should show up above the entire UI
+ ".components-draggable__clone": 1000000000,
+
+ // Should have higher index than the inset/underlay used for dragging
+ ".components-placeholder__fieldset": 1,
+ ".block-editor-block-list__block-edit .reusable-block-edit-panel *": 1,
+
+ // Show drop zone above most standard content, but below any overlays
+ ".components-drop-zone": 40,
+ ".components-drop-zone__content": 50,
+
+ // The block mover for floats should overlap the controls of adjacent blocks.
+ ".block-editor-block-list__block {core/image aligned left or right}": 21,
+
+ // Small screen inner blocks overlay must be displayed above drop zone,
+ // settings menu, and movers.
+ ".block-editor-inner-blocks.has-overlay::after": 60,
+
+ // The toolbar, when contextual, should be above any adjacent nested block click overlays.
+ ".block-editor-block-list__layout .reusable-block-edit-panel": 61,
+ ".block-editor-block-contextual-toolbar": 61,
+ ".editor-inner-blocks .block-editor-block-list__breadcrumb": 62,
+
+ // The block mover, particularly in nested contexts,
+ // should overlap most block content.
+ ".block-editor-block-list__block.is-{selected,hovered} .block-editor-block-mover": 61,
+
+ // Show sidebar above wp-admin navigation bar for mobile viewports:
+ // #wpadminbar { z-index: 99999 }
+ ".edit-post-sidebar": 100000,
+ ".edit-widgets-sidebar": 100000,
+ ".edit-post-layout .edit-post-post-publish-panel": 100001,
+ // For larger views, the wp-admin navbar dropdown should be at top of
+ // the Publish Post sidebar.
+ ".edit-post-layout .edit-post-post-publish-panel-break-medium": 99998,
+
+ // Show sidebar in greater than small viewports above editor related elements
+ // but bellow #adminmenuback { z-index: 100 }
+ ".edit-post-sidebar {greater than small}": 90,
+
+ // Show notices below expanded editor bar
+ // .edit-post-header { z-index: 30 }
+ ".components-notice-list": 29,
+
+
+ // Show snackbars above everything (similar to popovers)
+ ".components-snackbar-list": 100000,
+
+ // Show modal under the wp-admin menus and the popover
+ ".components-modal__screen-overlay": 100000,
+
+ // Show popovers above wp-admin menus and submenus and sidebar:
+ // #adminmenuwrap { z-index: 9990 }
+ ".components-popover": 1000000,
+
+ // ...Except for popovers immediately beneath wp-admin menu on large breakpoints
+ ".components-popover.block-editor-inserter__popover": 99998,
+ ".components-popover.table-of-contents__popover": 99998,
+ ".components-popover.block-editor-block-navigation__popover": 99998,
+ ".components-popover.edit-post-more-menu__content": 99998,
+ ".components-popover.block-editor-rich-text__inline-format-toolbar": 99998,
+
+ ".components-autocomplete__results": 1000000,
+
+ ".skip-to-selected-block": 100000,
+ ".edit-post-toggle-publish-panel": 100000,
+
+ // Show NUX tips above popovers, wp-admin menus, submenus, and sidebar:
+ ".nux-dot-tip": 1000001,
+
+ // Show tooltips above NUX tips, wp-admin menus, submenus, and sidebar:
+ ".components-tooltip": 1000002,
+
+ // Make sure corner handles are above side handles for ResizableBox component
+ ".components-resizable-box__side-handle": 1,
+ ".components-resizable-box__corner-handle": 2,
+
+ // Make sure block manager sticky category titles appear above the options
+ ".edit-post-manage-blocks-modal__category-title": 1
+);
+
+@function z-index( $key ) {
+ @if map-has-key( $z-layers, $key ) {
+ @return map-get( $z-layers, $key );
+ }
+
+ @error "Error: Specified z-index `#{$key}` does not exist in the mapping";
+}
diff --git a/gb-src/babel.config.js b/gb-src/babel.config.js
new file mode 100644
index 0000000000000..7679cc1e83704
--- /dev/null
+++ b/gb-src/babel.config.js
@@ -0,0 +1,8 @@
+module.exports = function( api ) {
+ api.cache( true );
+
+ return {
+ presets: [ '@wordpress/babel-preset-default' ],
+ plugins: [ 'babel-plugin-inline-json-import' ],
+ };
+};
diff --git a/gb-src/bin/build-plugin-zip.sh b/gb-src/bin/build-plugin-zip.sh
new file mode 100755
index 0000000000000..4db22d18b5acd
--- /dev/null
+++ b/gb-src/bin/build-plugin-zip.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+
+# Exit if any command fails.
+set -e
+
+# Change to the expected directory.
+cd "$(dirname "$0")"
+cd ..
+
+# Enable nicer messaging for build status.
+BLUE_BOLD='\033[1;34m';
+GREEN_BOLD='\033[1;32m';
+RED_BOLD='\033[1;31m';
+YELLOW_BOLD='\033[1;33m';
+COLOR_RESET='\033[0m';
+error () {
+ echo -e "\n${RED_BOLD}$1${COLOR_RESET}\n"
+}
+status () {
+ echo -e "\n${BLUE_BOLD}$1${COLOR_RESET}\n"
+}
+success () {
+ echo -e "\n${GREEN_BOLD}$1${COLOR_RESET}\n"
+}
+warning () {
+ echo -e "\n${YELLOW_BOLD}$1${COLOR_RESET}\n"
+}
+
+status "💃 Time to release Gutenberg 🕺"
+
+if [ -z "$NO_CHECKS" ]; then
+ # Make sure there are no changes in the working tree. Release builds should be
+ # traceable to a particular commit and reliably reproducible. (This is not
+ # totally true at the moment because we download nightly vendor scripts).
+ changed=
+ if ! git diff --exit-code > /dev/null; then
+ changed="file(s) modified"
+ elif ! git diff --cached --exit-code > /dev/null; then
+ changed="file(s) staged"
+ fi
+ if [ ! -z "$changed" ]; then
+ git status
+ error "ERROR: Cannot build plugin zip with dirty working tree. ☝️
+ Commit your changes and try again."
+ exit 1
+ fi
+
+ # Do a dry run of the repository reset. Prompting the user for a list of all
+ # files that will be removed should prevent them from losing important files!
+ status "Resetting the repository to pristine condition. ✨"
+ to_clean=$(git clean -xdf --dry-run)
+ if [ ! -z "$to_clean" ]; then
+ echo $to_clean
+ warning "🚨 About to delete everything above! Is this okay? 🚨"
+ echo -n "[y]es/[N]o: "
+ read answer
+ if [ "$answer" != "${answer#[Yy]}" ]; then
+ # Remove ignored files to reset repository to pristine condition. Previous
+ # test ensures that changed files abort the plugin build.
+ status "Cleaning working directory... 🛀"
+ git clean -xdf
+ else
+ error "Fair enough; aborting. Tidy up your repo and try again. 🙂"
+ exit 1
+ fi
+ fi
+fi
+
+# Download all vendor scripts
+status "Downloading remote vendor scripts... 🛵"
+vendor_scripts=""
+# Using `command | while read...` is more typical, but the inside of the `while`
+# loop will run under a separate process this way, meaning that it cannot
+# modify $vendor_scripts. See: https://stackoverflow.com/a/16855194
+exec 3< <(
+ # Get minified versions of vendor scripts.
+ php bin/get-vendor-scripts.php
+ # Get non-minified versions of vendor scripts (for SCRIPT_DEBUG).
+ php bin/get-vendor-scripts.php debug
+)
+while IFS='|' read -u 3 url filename; do
+ echo "$url"
+ echo -n " > vendor/$filename ... "
+ http_status=$( curl \
+ --location \
+ --silent \
+ "$url" \
+ --output "vendor/_download.tmp.js" \
+ --write-out "%{http_code}"
+ )
+ if [ "$http_status" != 200 ]; then
+ error "HTTP $http_status"
+ exit 1
+ fi
+ mv -f "vendor/_download.tmp.js" "vendor/$filename"
+ echo -e "${GREEN_BOLD}done!${COLOR_RESET}"
+ vendor_scripts="$vendor_scripts vendor/$filename"
+done
+
+# Run the build.
+status "Installing dependencies... 📦"
+PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install
+status "Generating build... 👷♀️"
+npm run build
+
+# Temporarily modify `gutenberg.php` with production constants defined. Use a
+# temp file because `bin/generate-gutenberg-php.php` reads from `gutenberg.php`
+# so we need to avoid writing to that file at the same time.
+php bin/generate-gutenberg-php.php > gutenberg.tmp.php
+mv gutenberg.tmp.php gutenberg.php
+
+build_files=$(ls build/*/*.{js,css,asset.php} build/block-library/blocks/*.php)
+
+# Generate the plugin zip file.
+status "Creating archive... 🎁"
+zip -r gutenberg.zip \
+ gutenberg.php \
+ lib/*.php \
+ packages/block-serialization-default-parser/*.php \
+ post-content.php \
+ $vendor_scripts \
+ $build_files \
+ README.md
+
+# Reset `gutenberg.php`.
+git checkout gutenberg.php
+
+success "Done. You've built Gutenberg! 🎉 "
diff --git a/gb-src/bin/commander.js b/gb-src/bin/commander.js
new file mode 100755
index 0000000000000..9edd90821d3bd
--- /dev/null
+++ b/gb-src/bin/commander.js
@@ -0,0 +1,641 @@
+#!/usr/bin/env node
+
+/* eslint-disable no-console */
+
+// Dependencies
+const path = require( 'path' );
+const program = require( 'commander' );
+const inquirer = require( 'inquirer' );
+const semver = require( 'semver' );
+const chalk = require( 'chalk' );
+const fs = require( 'fs-extra' );
+const SimpleGit = require( 'simple-git/promise' );
+const childProcess = require( 'child_process' );
+const Octokit = require( '@octokit/rest' );
+const os = require( 'os' );
+const uuid = require( 'uuid/v4' );
+
+// Config
+const gitRepoOwner = 'WordPress';
+const gitRepoURL = 'git@github.com:' + gitRepoOwner + '/gutenberg.git';
+const svnRepoURL = 'https://plugins.svn.wordpress.org/gutenberg';
+
+// Working Directories
+const gitWorkingDirectoryPath = path.join( os.tmpdir(), uuid() );
+const svnWorkingDirectoryPath = path.join( os.tmpdir(), uuid() );
+
+// UI
+const error = chalk.bold.red;
+const warning = chalk.bold.keyword( 'orange' );
+const success = chalk.bold.green;
+
+// Utils
+
+/**
+ * Asks the user for a confirmation to continue or abort otherwise
+ *
+ * @param {string} message Confirmation message.
+ * @param {boolean} isDefault Default reply.
+ * @param {string} abortMessage Abort message.
+ */
+async function askForConfirmationToContinue( message, isDefault = true, abortMessage = 'Aborting.' ) {
+ const { isReady } = await inquirer.prompt( [ {
+ type: 'confirm',
+ name: 'isReady',
+ default: isDefault,
+ message,
+ } ] );
+
+ if ( ! isReady ) {
+ console.log( error( '\n' + abortMessage ) );
+ process.exit( 1 );
+ }
+}
+
+/**
+ * Common logic wrapping a step in the process.
+ *
+ * @param {string} name Step name.
+ * @param {string} abortMessage Abort message.
+ * @param {Function} handler Step logic.
+ */
+async function runStep( name, abortMessage, handler ) {
+ try {
+ await handler();
+ } catch ( exception ) {
+ console.log(
+ error( 'The following error happened during the "' + warning( name ) + '" step:' ) + '\n\n',
+ exception,
+ error( '\n\n' + abortMessage )
+ );
+
+ process.exit( 1 );
+ }
+}
+
+/**
+ * Utility to run a child script
+ *
+ * @param {string} script Script to run.
+ * @param {string} cwd Working directory.
+ */
+function runShellScript( script, cwd ) {
+ childProcess.execSync( script, {
+ cwd,
+ env: {
+ NO_CHECKS: true,
+ PATH: process.env.PATH,
+ },
+ stdio: [ 'inherit', 'ignore', 'inherit' ],
+ } );
+}
+
+// Steps
+
+/**
+ * Clone the Gutenberg repository to the working directory.
+ *
+ * @param {string} abortMessage Abort message.
+ */
+async function runGitRepositoryCloneStep( abortMessage ) {
+ // Cloning the repository
+ await runStep( 'Cloning the Git repository', abortMessage, async () => {
+ console.log( '>> Cloning the Git repository' );
+ const simpleGit = SimpleGit();
+ await simpleGit.clone( gitRepoURL, gitWorkingDirectoryPath );
+ console.log( '>> The Gutenberg Git repository has been successfully cloned in the following temporary folder: ' + success( gitWorkingDirectoryPath ) );
+ } );
+}
+
+/**
+ * Fetching the SVN Gutenberg repository to the working directory.
+ *
+ * @param {string} abortMessage Abort message.
+ */
+async function runSvnRepositoryCloneStep( abortMessage ) {
+ // Cloning the repository
+ await runStep( 'Fetching the SVN repository', abortMessage, async () => {
+ console.log( '>> Fetching the SVN repository' );
+ runShellScript( 'svn checkout ' + svnRepoURL + '/trunk ' + svnWorkingDirectoryPath );
+ console.log( '>> The Gutenberg SVN repository has been successfully fetched in the following temporary folder: ' + success( svnWorkingDirectoryPath ) );
+ } );
+}
+
+/**
+ * Updates and commits the content of the SVN repo using the new plugin ZIP.
+ *
+ * @param {string} version Version.
+ * @param {string} changelog Changelog.
+ * @param {string} abortMessage Abort Message.
+ */
+async function runUpdateTrunkContentStep( version, changelog, abortMessage ) {
+ // Updating the content of the svn
+ await runStep( 'Updating trunk content', abortMessage, async () => {
+ console.log( '>> Replacing trunk content using the new plugin ZIP' );
+
+ // Delete everything except readme.txt and changelog.txt
+ runShellScript( 'find . -maxdepth 1 -not -name "changelog.txt" -not -name "readme.txt" -not -name ".svn" -not -name "." -not -name ".." -exec rm -rf {} +', svnWorkingDirectoryPath );
+
+ // Update the content using the plugin ZIP
+ const gutenbergZipPath = gitWorkingDirectoryPath + '/gutenberg.zip';
+ runShellScript( 'unzip ' + gutenbergZipPath + ' -d ' + svnWorkingDirectoryPath );
+
+ console.log( '>> Updating the changelog in readme.txt and changelog.txt' );
+
+ // Update the content of the readme.txt file
+ const readmePath = svnWorkingDirectoryPath + '/readme.txt';
+ const readmeFileContent = fs.readFileSync( readmePath, 'utf8' );
+ const newReadmeContent =
+ readmeFileContent.substr( 0, readmeFileContent.indexOf( '== Changelog ==' ) ) +
+ '== Changelog ==\n\n' +
+ changelog + '\n';
+ fs.writeFileSync( readmePath, newReadmeContent );
+
+ // Update the content of the changelog.txt file
+ const changelogPath = svnWorkingDirectoryPath + '/changelog.txt';
+ const changelogFileContent = fs.readFileSync( changelogPath, 'utf8' );
+ const newChangelogContent =
+ '== Changelog ==\n\n' +
+ '= ' + version + ' =\n\n' +
+ changelog +
+ changelogFileContent.substr( changelogFileContent.indexOf( '== Changelog ==' ) + 16 );
+ fs.writeFileSync( changelogPath, newChangelogContent );
+
+ // Commit the content changes
+ runShellScript( "svn st | grep '^\?' | awk '{print $2}' | xargs svn add", svnWorkingDirectoryPath );
+ runShellScript( "svn st | grep '^!' | awk '{print $2}' | xargs svn rm", svnWorkingDirectoryPath );
+ await askForConfirmationToContinue(
+ 'Trunk content has been updated, please check the SVN diff. Commit the changes?',
+ true,
+ abortMessage
+ );
+
+ runShellScript( 'svn commit -m "Committing Gutenberg version ' + version + '"', svnWorkingDirectoryPath );
+
+ console.log( '>> Trunk has been successfully updated' );
+ } );
+}
+
+/**
+ * Creates a new SVN Tag
+ *
+ * @param {string} version Version.
+ * @param {string} abortMessage Abort Message.
+ */
+async function runSvnTagStep( version, abortMessage ) {
+ await runStep( 'Creating the SVN Tag', abortMessage, async () => {
+ await askForConfirmationToContinue(
+ 'Proceed with the creation of the SVN Tag?',
+ true,
+ abortMessage
+ );
+ runShellScript( 'svn cp ' + svnRepoURL + '/trunk ' + svnRepoURL + '/tags/' + version + ' -m "Tagging Gutenberg version ' + version + '"' );
+
+ console.log( '>> The SVN ' + success( version ) + ' tag has been successfully created' );
+ } );
+}
+
+/**
+ * Updates the stable version of the plugin in the SVN repository.
+ *
+ * @param {string} version Version.
+ * @param {string} abortMessage Abort Message.
+ */
+async function updateThePluginStableVersion( version, abortMessage ) {
+ // Updating the content of the svn
+ await runStep( 'Updating the plugin\'s stable version', abortMessage, async () => {
+ const readmePath = svnWorkingDirectoryPath + '/readme.txt';
+ const readmeFileContent = fs.readFileSync( readmePath, 'utf8' );
+ const newReadmeContent = readmeFileContent.replace(
+ /Stable tag: [0-9]+.[0-9]+.[0-9]+\s*\n/,
+ 'Stable tag: ' + version + '\n'
+ );
+ fs.writeFileSync( readmePath, newReadmeContent );
+
+ // Commit the content changes
+ await askForConfirmationToContinue(
+ 'The stable version is updated in the readme.txt file. Commit the changes?',
+ true,
+ abortMessage
+ );
+
+ runShellScript( 'svn commit -m "Releasing Gutenberg version ' + version + '"', svnWorkingDirectoryPath );
+
+ console.log( '>> Stable version updated successfully' );
+ } );
+}
+
+/**
+ * Clean the working directory.
+ *
+ * @param {string} abortMessage Abort message.
+ */
+async function runCleanLocalCloneStep( abortMessage ) {
+ await runStep( 'Cleaning the temporary folder', abortMessage, async () => {
+ await fs.remove( gitWorkingDirectoryPath );
+ await fs.remove( svnWorkingDirectoryPath );
+ } );
+}
+
+/**
+ * Creates a new release branch based on the last package.json version
+ * and chooses the next RC version number.
+ *
+ * @param {string} abortMessage Abort Message.
+ *
+ * @return {Object} chosen version and versionLabels.
+ */
+async function runReleaseBranchCreationStep( abortMessage ) {
+ let version, releaseBranch, versionLabel;
+ await runStep( 'Creating the release branch', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ const packageJsonPath = gitWorkingDirectoryPath + '/package.json';
+ const packageJson = require( packageJsonPath );
+ const parsedVersion = semver.parse( packageJson.version );
+
+ // Follow the WordPress version guidelines to compute the version to be used
+ // By default, increase the "minor" number but if we reach 9, bump to the next major.
+ if ( parsedVersion.minor === 9 ) {
+ version = ( parsedVersion.major + 1 ) + '.0.0-rc.1';
+ releaseBranch = 'release/' + ( parsedVersion.major + 1 ) + '.0';
+ versionLabel = ( parsedVersion.major + 1 ) + '.0.0 RC1';
+ } else {
+ version = parsedVersion.major + '.' + ( parsedVersion.minor + 1 ) + '.0-rc.1';
+ releaseBranch = 'release/' + parsedVersion.major + '.' + ( parsedVersion.minor + 1 );
+ versionLabel = parsedVersion.major + '.' + ( parsedVersion.minor + 1 ) + '.0 RC1';
+ }
+ await askForConfirmationToContinue(
+ 'The Plugin version to be used is ' + success( version ) + '. Proceed with the creation of the release branch?',
+ true,
+ abortMessage
+ );
+
+ // Creating the release branch
+ await simpleGit.checkoutLocalBranch( releaseBranch );
+ console.log( '>> The local release branch ' + success( releaseBranch ) + ' has been successfully created.' );
+ } );
+
+ return {
+ version,
+ versionLabel,
+ releaseBranch,
+ };
+}
+
+/**
+ * Checkouts out the release branch and chooses a stable version number.
+ *
+ * @param {string} abortMessage Abort Message.
+ *
+ * @return {Object} chosen version and versionLabels.
+ */
+async function runReleaseBranchCheckoutStep( abortMessage ) {
+ let releaseBranch, version;
+ await runStep( 'Getting into the release branch', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ const packageJsonPath = gitWorkingDirectoryPath + '/package.json';
+ const masterPackageJson = require( packageJsonPath );
+ const masterParsedVersion = semver.parse( masterPackageJson.version );
+ releaseBranch = 'release/' + masterParsedVersion.major + '.' + masterParsedVersion.minor;
+
+ // Creating the release branch
+ await simpleGit.checkout( releaseBranch );
+ console.log( '>> The local release branch ' + success( releaseBranch ) + ' has been successfully checked out.' );
+
+ const releaseBranchPackageJson = require( packageJsonPath );
+ const releaseBranchParsedVersion = semver.parse( releaseBranchPackageJson.version );
+
+ if ( releaseBranchParsedVersion.prerelease && releaseBranchParsedVersion.prerelease.length ) {
+ version = releaseBranchParsedVersion.major + '.' + releaseBranchParsedVersion.minor + '.' + releaseBranchParsedVersion.patch;
+ } else {
+ version = releaseBranchParsedVersion.major + '.' + releaseBranchParsedVersion.minor + '.' + ( releaseBranchParsedVersion.patch + 1 );
+ }
+
+ await askForConfirmationToContinue(
+ 'The Version to release is ' + success( version ) + '. Proceed?',
+ true,
+ abortMessage
+ );
+ } );
+
+ return {
+ version,
+ versionLabel: version,
+ releaseBranch,
+ };
+}
+
+/**
+ * Bump the version in the different files (package.json, package-lock.json, gutenberg.php)
+ * and commit the changes.
+ *
+ * @param {string} version Version to use.
+ * @param {string} abortMessage Abort message.
+ *
+ * @return {string} hash of the version bump commit.
+ */
+async function runBumpPluginVersionAndCommitStep( version, abortMessage ) {
+ let commitHash;
+ await runStep( 'Updating the plugin version', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ const packageJsonPath = gitWorkingDirectoryPath + '/package.json';
+ const packageLockPath = gitWorkingDirectoryPath + '/package-lock.json';
+ const pluginFilePath = gitWorkingDirectoryPath + '/gutenberg.php';
+ const packageJson = require( packageJsonPath );
+ const packageLock = require( packageLockPath );
+ const newPackageJson = {
+ ...packageJson,
+ version,
+ };
+ fs.writeFileSync( packageJsonPath, JSON.stringify( newPackageJson, null, '\t' ) + '\n' );
+ const newPackageLock = {
+ ...packageLock,
+ version,
+ };
+ fs.writeFileSync( packageLockPath, JSON.stringify( newPackageLock, null, '\t' ) + '\n' );
+ const content = fs.readFileSync( pluginFilePath, 'utf8' );
+ fs.writeFileSync( pluginFilePath, content.replace( ' * Version: ' + packageJson.version, ' * Version: ' + version ) );
+ console.log( '>> The plugin version has been updated successfully.' );
+
+ // Commit the version bump
+ await askForConfirmationToContinue(
+ 'Please check the diff. Proceed with the version bump commit?',
+ true,
+ abortMessage
+ );
+ await simpleGit.add( [
+ packageJsonPath,
+ packageLockPath,
+ pluginFilePath,
+ ] );
+ const commitData = await simpleGit.commit( 'Bump plugin version to ' + version );
+ commitHash = commitData.commit;
+ console.log( '>> The plugin version bump has been commited successfully.' );
+ } );
+
+ return commitHash;
+}
+
+/**
+ * Run the Plugin ZIP Creation step.
+ *
+ * @param {string} abortMessage Abort message.
+ */
+async function runPluginZIPCreationStep( abortMessage ) {
+ await runStep( 'Plugin ZIP creation', abortMessage, async () => {
+ const gutenbergZipPath = gitWorkingDirectoryPath + '/gutenberg.zip';
+ await askForConfirmationToContinue(
+ 'Proceed and build the plugin ZIP? (It takes a few minutes)',
+ true,
+ abortMessage
+ );
+ runShellScript( '/bin/bash bin/build-plugin-zip.sh', gitWorkingDirectoryPath );
+
+ console.log( '>> The plugin ZIP has been built successfully. Path: ' + success( gutenbergZipPath ) );
+ } );
+}
+
+/**
+ * Create a local Git Tag.
+ *
+ * @param {string} version Version to use.
+ * @param {string} abortMessage Abort message.
+ */
+async function runCreateGitTagStep( version, abortMessage ) {
+ await runStep( 'Creating the git tag', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ await askForConfirmationToContinue(
+ 'Proceed with the creation of the git tag?',
+ true,
+ abortMessage
+ );
+ await simpleGit.addTag( 'v' + version );
+ console.log( '>> The ' + success( 'v' + version ) + ' tag has been created successfully.' );
+ } );
+}
+
+/**
+ * Push the local Git Changes and Tags to the remote repository.
+ *
+ * @param {string} releaseBranch Release branch name.
+ * @param {string} abortMessage Abort message.
+ */
+async function runPushGitChangesStep( releaseBranch, abortMessage ) {
+ await runStep( 'Pushing the release branch and the tag', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ await askForConfirmationToContinue(
+ 'The release branch and the tag are going to be pushed to the remote repository. Continue?',
+ true,
+ abortMessage
+ );
+ await simpleGit.push( 'origin', releaseBranch );
+ await simpleGit.pushTags( 'origin' );
+ } );
+}
+
+/**
+ * Creates the github release and uploads the Gutenberg ZIP file into it.
+ *
+ * @param {string} version Released version.
+ * @param {string} versionLabel Label of the released Version.
+ * @param {boolean} isPrerelease is a pre-release.
+ * @param {string} abortMessage Abort message.
+ *
+ * @return {Object} Github release object.
+ */
+async function runGithubReleaseStep( version, versionLabel, isPrerelease, abortMessage ) {
+ let octokit;
+ let release;
+ await runStep( 'Creating the GitHub release', abortMessage, async () => {
+ await askForConfirmationToContinue(
+ 'Proceed with the creation of the GitHub release?',
+ true,
+ abortMessage
+ );
+ const { changelog } = await inquirer.prompt( [ {
+ type: 'editor',
+ name: 'changelog',
+ message: 'Please provide the CHANGELOG of the release (markdown)',
+ } ] );
+
+ const { token } = await inquirer.prompt( [ {
+ type: 'input',
+ name: 'token',
+ message: 'Please provide a GitHub personal authentication token. Navigate to ' + success( 'https://github.com/settings/tokens/new?scopes=repo,admin:org,write:packages' ) + ' to create one.',
+ } ] );
+
+ octokit = new Octokit( {
+ auth: token,
+ } );
+
+ const releaseData = await octokit.repos.createRelease( {
+ owner: gitRepoOwner,
+ repo: 'gutenberg',
+ tag_name: 'v' + version,
+ name: versionLabel,
+ body: changelog,
+ prerelease: isPrerelease,
+ } );
+ release = releaseData.data;
+
+ console.log( '>> The GitHub release has been created.' );
+ } );
+ abortMessage = abortMessage + ' Make sure to remove the the GitHub release as well.';
+
+ // Uploading the Gutenberg Zip to the release
+ await runStep( 'Uploading the plugin ZIP', abortMessage, async () => {
+ const gutenbergZipPath = gitWorkingDirectoryPath + '/gutenberg.zip';
+ const filestats = fs.statSync( gutenbergZipPath );
+ await octokit.repos.uploadReleaseAsset( {
+ url: release.upload_url,
+ headers: {
+ 'content-length': filestats.size,
+ 'content-type': 'application/zip',
+ },
+ name: 'gutenberg.zip',
+ file: fs.createReadStream( gutenbergZipPath ),
+ } );
+ console.log( '>> The plugin ZIP has been successfully uploaded.' );
+ } );
+
+ console.log( '>> The GitHub release is available here: ' + success( release.html_url ) );
+
+ return release;
+}
+
+/**
+ * Cherry-picks the version bump commit into master.
+ *
+ * @param {string} commitHash Commit to cherry-pick.
+ * @param {string} abortMessage Abort message.
+ */
+async function runCherrypickBumpCommitIntoMasterStep( commitHash, abortMessage ) {
+ await runStep( 'Cherry-picking the bump commit into master', abortMessage, async () => {
+ const simpleGit = SimpleGit( gitWorkingDirectoryPath );
+ await askForConfirmationToContinue(
+ 'The plugin is now released. Proceed with the version bump in the master branch?',
+ true,
+ abortMessage
+ );
+ await simpleGit.fetch();
+ await simpleGit.reset( 'hard' );
+ await simpleGit.checkout( 'master' );
+ await simpleGit.pull( 'origin', 'master' );
+ await simpleGit.raw( [ 'cherry-pick', commitHash ] );
+ await simpleGit.push( 'origin', 'master' );
+ } );
+}
+
+/**
+ * Release a new Gutenberg version.
+ *
+ * @param {boolean} isRC Whether it's an RC release or not.
+ *
+ * @return {Object} Github release object.
+ */
+async function releasePlugin( isRC = true ) {
+ // This is a variable that contains the abort message shown when the script is aborted.
+ let abortMessage = 'Aborting!';
+ await askForConfirmationToContinue( 'Ready to go? ' );
+
+ // Cloning the Git repository
+ await runGitRepositoryCloneStep( abortMessage );
+
+ // Creating the release branch
+ const { version, versionLabel, releaseBranch } = isRC ?
+ await runReleaseBranchCreationStep( abortMessage ) :
+ await runReleaseBranchCheckoutStep( abortMessage );
+
+ // Bumping the version and commit.
+ const commitHash = await runBumpPluginVersionAndCommitStep( version, abortMessage );
+
+ // Plugin ZIP creation
+ await runPluginZIPCreationStep();
+
+ // Creating the git tag
+ await runCreateGitTagStep( version, abortMessage );
+
+ // Push the local changes
+ await runPushGitChangesStep( releaseBranch, abortMessage );
+ abortMessage = 'Aborting! Make sure to ' + isRC ? 'remove' : 'reset' + ' the remote release branch and remove the git tag.';
+
+ // Creating the GitHub Release
+ const release = await runGithubReleaseStep( version, versionLabel, isRC, abortMessage );
+ abortMessage = 'Aborting! Make sure to manually cherry-pick the ' + success( commitHash ) + ' commit to the master branch.';
+ if ( ! isRC ) {
+ abortMessage += ' Make sure to perform the SVN release manually as well.';
+ }
+
+ // Cherry-picking the bump commit into master
+ await runCherrypickBumpCommitIntoMasterStep( commitHash, abortMessage );
+
+ if ( ! isRC ) {
+ abortMessage = 'Aborting! The GitHub release is done. Make sure to perform the SVN release manually.';
+
+ await askForConfirmationToContinue( 'The GitHub release is complete. Proceed with the SVN release? ', abortMessage );
+
+ // Fetching the SVN repository
+ await runSvnRepositoryCloneStep( abortMessage );
+
+ // Updating the SVN trunk content
+ await runUpdateTrunkContentStep( version, release.body, abortMessage );
+
+ abortMessage = 'Aborting! The GitHub release is done, SVN trunk updated. Make sure to create the SVN tag and update the stable version manually.';
+ await runSvnTagStep( version, abortMessage );
+
+ abortMessage = 'Aborting! The GitHub release is done, SVN tagged. Make sure to update the stable version manually.';
+ await updateThePluginStableVersion( version, abortMessage );
+ }
+
+ abortMessage = 'Aborting! The release is finished though.';
+ await runCleanLocalCloneStep( abortMessage );
+
+ return release;
+}
+
+program
+ .command( 'release-plugin-rc' )
+ .alias( 'rc' )
+ .description( 'Release an RC version of the plugin (supports only rc.1 for now)' )
+ .action( async () => {
+ console.log(
+ chalk.bold( '💃 Time to release Gutenberg 🕺\n\n' ),
+ 'Welcome! This tool is going to help you release a new RC version of the Gutenberg Plugin.\n',
+ 'It goes through different steps : creating the release branch, bumping the plugin version, tagging and creating the GitHub release, building the ZIP...\n',
+ 'To perform a release you\'ll have to be a member of the Gutenberg Core Team.\n'
+ );
+
+ const release = await releasePlugin( true );
+
+ console.log(
+ '\n>> 🎉 The Gutenberg version ' + success( release.name ) + ' has been successfully released.\n',
+ 'You can access the GitHub release here: ' + success( release.html_url ) + '\n',
+ 'Thanks for performing the release!'
+ );
+ } );
+
+program
+ .command( 'release-plugin-stable' )
+ .alias( 'stable' )
+ .description( 'Release a stable version of the plugin' )
+ .action( async () => {
+ console.log(
+ chalk.bold( '💃 Time to release Gutenberg 🕺\n\n' ),
+ 'Welcome! This tool is going to help you release a new stable version of the Gutenberg Plugin.\n',
+ 'It goes through different steps : bumping the plugin version, tagging and creating the GitHub release, building the ZIP, pushing the release to the SVN repository...\n',
+ 'To perform a release you\'ll have to be a member of the Gutenberg Core Team.\n'
+ );
+
+ const release = await releasePlugin( false );
+
+ console.log(
+ '\n>> 🎉 The Gutenberg ' + success( release.name ) + ' has been successfully released.\n',
+ 'You can access the GitHub release here: ' + success( release.html_url ) + '\n',
+ 'In a few minutes, you\'ll be able to update the plugin from the WordPress repository.\n',
+ 'Thanks for performing the release! and don\'t forget to publish the release post.'
+ );
+ } );
+
+program.parse( process.argv );
+
+/* eslint-enable no-console */
diff --git a/gb-src/bin/docker-compose.override.yml.template b/gb-src/bin/docker-compose.override.yml.template
new file mode 100644
index 0000000000000..465211fe7a4a6
--- /dev/null
+++ b/gb-src/bin/docker-compose.override.yml.template
@@ -0,0 +1,17 @@
+services:
+ wordpress-develop:
+ volumes:
+ - %PLUGIN_MOUNT_DIR%:/var/www/${LOCAL_DIR-src}/wp-content/plugins/%PLUGIN_INSTALL_DIR%
+ - %PLUGIN_MOUNT_DIR%/packages/e2e-tests/plugins:/var/www/${LOCAL_DIR-src}/wp-content/plugins/gutenberg-test-plugins
+ - %PLUGIN_MOUNT_DIR%/packages/e2e-tests/mu-plugins:/var/www/${LOCAL_DIR-src}/wp-content/mu-plugins
+ php:
+ volumes:
+ - %PLUGIN_MOUNT_DIR%:/var/www/${LOCAL_DIR-src}/wp-content/plugins/%PLUGIN_INSTALL_DIR%
+ - %PLUGIN_MOUNT_DIR%/packages/e2e-tests/plugins:/var/www/${LOCAL_DIR-src}/wp-content/plugins/gutenberg-test-plugins
+ - %PLUGIN_MOUNT_DIR%/packages/e2e-tests/mu-plugins:/var/www/${LOCAL_DIR-src}/wp-content/mu-plugins
+ cli:
+ volumes:
+ - %PLUGIN_MOUNT_DIR%:/var/www/${LOCAL_DIR-src}/wp-content/plugins/%PLUGIN_INSTALL_DIR%
+ phpunit:
+ volumes:
+ - %PLUGIN_MOUNT_DIR%:/var/www/${LOCAL_DIR-src}/wp-content/plugins/%PLUGIN_INSTALL_DIR%
diff --git a/gb-src/bin/generate-gutenberg-php.php b/gb-src/bin/generate-gutenberg-php.php
new file mode 100755
index 0000000000000..fc9bf51ef3cab
--- /dev/null
+++ b/gb-src/bin/generate-gutenberg-php.php
@@ -0,0 +1,62 @@
+#!/usr/bin/env php
+= 0;
+}
+
+function flattenUnary( expression ) {
+ const shouldWrap = isGroup( expression );
+ const inner = flatten( expression );
+ return shouldWrap ? '(' + inner + ')' : inner;
+}
+
+function flatten( expression ) {
+ switch ( expression.type ) {
+ // Terminal
+ case 'any':
+ return '.';
+ case 'rule_ref':
+ return expression.name;
+ case 'literal':
+ return '"' + escape( expression.value ) + '"';
+ case 'class':
+ return (
+ '[' + ( expression.inverted ? '^' : '' ) +
+ expression.parts.map( ( part ) =>
+ escape( Array.isArray( part ) ? part.join( '-' ) : part )
+ ).join( '' ) +
+ ']' + ( expression.ignoreCase ? 'i' : '' )
+ );
+
+ // Unary
+ case 'zero_or_more':
+ return flattenUnary( expression.expression ) + '*';
+ case 'one_or_more':
+ return flattenUnary( expression.expression ) + '+';
+ case 'optional':
+ return flattenUnary( expression.expression ) + '?';
+ case 'simple_not':
+ return '!' + flattenUnary( expression.expression );
+
+ // Other groups
+ case 'sequence':
+ return expression.elements.map( flatten ).join( ' ' );
+ case 'choice':
+ const sep = expression.isRuleTop ? '\n / ' : ' / ';
+ return expression.alternatives.map( flatten ).join( sep );
+ case 'group':
+ return '(' + flatten( expression.expression ) + ')';
+ case 'text':
+ // Avoid double parentheses
+ const inner = flatten( expression.expression );
+ const shouldWrap = inner.indexOf( '(' ) !== 0;
+ return shouldWrap ? '$(' + inner + ')' : '$' + inner;
+ case 'action':
+ case 'labeled':
+ case 'named':
+ return flatten( expression.expression );
+
+ // Top-level formatting
+ case 'grammar':
+ return `
`;
+
+ default:
+ throw new Error( JSON.stringify( expression ) );
+ }
+}
+
+fs.writeFileSync(
+ path.join( __dirname, '..', 'docs', 'grammar.md' ), `
+# Block Grammar
+
+${ flatten( grammar ) }
+` );
diff --git a/gb-src/bin/get-server-blocks.php b/gb-src/bin/get-server-blocks.php
new file mode 100755
index 0000000000000..164fafd467db9
--- /dev/null
+++ b/gb-src/bin/get-server-blocks.php
@@ -0,0 +1,39 @@
+#!/usr/bin/env php
+ 1 && 'debug' === strtolower( $argv[1] ) );
+
+// Hacks to get lib/client-assets.php to load.
+define( 'ABSPATH', dirname( dirname( __FILE__ ) ) );
+
+/**
+ * Hi, phpcs
+ */
+function add_action() {}
+
+/**
+ * Hi, phpcs
+ */
+function add_filter() {}
+
+/**
+ * Hi, phpcs
+ */
+function wp_add_inline_script() {}
+
+// Instead of loading script files, just show how they need to be loaded.
+define( 'GUTENBERG_LIST_VENDOR_ASSETS', true );
+
+require_once dirname( dirname( __FILE__ ) ) . '/lib/client-assets.php';
+
+gutenberg_register_vendor_scripts();
diff --git a/gb-src/bin/packages/build-worker.js b/gb-src/bin/packages/build-worker.js
new file mode 100644
index 0000000000000..7e3e636c013a1
--- /dev/null
+++ b/gb-src/bin/packages/build-worker.js
@@ -0,0 +1,159 @@
+/**
+ * External dependencies
+ */
+const { promisify } = require( 'util' );
+const fs = require( 'fs' );
+const path = require( 'path' );
+const babel = require( '@babel/core' );
+const makeDir = require( 'make-dir' );
+const sass = require( 'node-sass' );
+const postcss = require( 'postcss' );
+
+/**
+ * Internal dependencies
+ */
+const getBabelConfig = require( './get-babel-config' );
+
+/**
+ * Path to packages directory.
+ *
+ * @type {string}
+ */
+const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );
+
+/**
+ * Mapping of JavaScript environments to corresponding build output.
+ *
+ * @type {Object}
+ */
+const JS_ENVIRONMENTS = {
+ main: 'build',
+ module: 'build-module',
+};
+
+/**
+ * Promisified fs.readFile.
+ *
+ * @type {Function}
+ */
+const readFile = promisify( fs.readFile );
+
+/**
+ * Promisified fs.writeFile.
+ *
+ * @type {Function}
+ */
+const writeFile = promisify( fs.writeFile );
+
+/**
+ * Promisified sass.render.
+ *
+ * @type {Function}
+ */
+const renderSass = promisify( sass.render );
+
+/**
+ * Get the package name for a specified file
+ *
+ * @param {string} file File name
+ * @return {string} Package name
+ */
+function getPackageName( file ) {
+ return path.relative( PACKAGES_DIR, file ).split( path.sep )[ 0 ];
+}
+
+/**
+ * Get Build Path for a specified file.
+ *
+ * @param {string} file File to build
+ * @param {string} buildFolder Output folder
+ * @return {string} Build path
+ */
+function getBuildPath( file, buildFolder ) {
+ const pkgName = getPackageName( file );
+ const pkgSrcPath = path.resolve( PACKAGES_DIR, pkgName, 'src' );
+ const pkgBuildPath = path.resolve( PACKAGES_DIR, pkgName, buildFolder );
+ const relativeToSrcPath = path.relative( pkgSrcPath, file );
+ return path.resolve( pkgBuildPath, relativeToSrcPath );
+}
+
+/**
+ * Object of build tasks per file extension.
+ *
+ * @type {Object}
+ */
+const BUILD_TASK_BY_EXTENSION = {
+ async '.scss'( file ) {
+ const outputFile = getBuildPath( file.replace( '.scss', '.css' ), 'build-style' );
+ const outputFileRTL = getBuildPath( file.replace( '.scss', '-rtl.css' ), 'build-style' );
+
+ const [ , contents ] = await Promise.all( [
+ makeDir( path.dirname( outputFile ) ),
+ readFile( file, 'utf8' ),
+ ] );
+
+ const builtSass = await renderSass( {
+ file,
+ includePaths: [ path.resolve( __dirname, '../../assets/stylesheets' ) ],
+ data: (
+ [
+ 'colors',
+ 'breakpoints',
+ 'variables',
+ 'mixins',
+ 'animations',
+ 'z-index',
+ ].map( ( imported ) => `@import "${ imported }";` ).join( ' ' ) +
+ contents
+ ),
+ } );
+
+ const result = await postcss( require( './post-css-config' ) ).process( builtSass.css, {
+ from: 'src/app.css',
+ to: 'dest/app.css',
+ } );
+
+ const resultRTL = await postcss( [ require( 'rtlcss' )() ] ).process( result.css, {
+ from: 'src/app.css',
+ to: 'dest/app.css',
+ } );
+
+ await Promise.all( [
+ writeFile( outputFile, result.css ),
+ writeFile( outputFileRTL, resultRTL.css ),
+ ] );
+ },
+
+ async '.js'( file ) {
+ for ( const [ environment, buildDir ] of Object.entries( JS_ENVIRONMENTS ) ) {
+ const destPath = getBuildPath( file, buildDir );
+ const babelOptions = getBabelConfig( environment, file.replace( PACKAGES_DIR, '@wordpress' ) );
+
+ const [ , transformed ] = await Promise.all( [
+ makeDir( path.dirname( destPath ) ),
+ babel.transformFileAsync( file, babelOptions ),
+ ] );
+
+ await Promise.all( [
+ writeFile( destPath + '.map', JSON.stringify( transformed.map ) ),
+ writeFile( destPath, transformed.code + '\n//# sourceMappingURL=' + path.basename( destPath ) + '.map' ),
+ ] );
+ }
+ },
+};
+
+module.exports = async ( file, callback ) => {
+ const extension = path.extname( file );
+ const task = BUILD_TASK_BY_EXTENSION[ extension ];
+
+ if ( ! task ) {
+ return;
+ }
+
+ try {
+ await task( file );
+ callback();
+ } catch ( error ) {
+ callback( error );
+ }
+};
diff --git a/gb-src/bin/packages/build.js b/gb-src/bin/packages/build.js
new file mode 100755
index 0000000000000..4c4883c152349
--- /dev/null
+++ b/gb-src/bin/packages/build.js
@@ -0,0 +1,190 @@
+/* eslint-disable no-console */
+
+/**
+ * External dependencies
+ */
+const path = require( 'path' );
+const glob = require( 'fast-glob' );
+const ProgressBar = require( 'progress' );
+const workerFarm = require( 'worker-farm' );
+const { Readable, Transform } = require( 'stream' );
+
+const files = process.argv.slice( 2 );
+
+/**
+ * Path to packages directory.
+ *
+ * @type {string}
+ */
+const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );
+
+/**
+ * Get the package name for a specified file
+ *
+ * @param {string} file File name
+ * @return {string} Package name
+ */
+function getPackageName( file ) {
+ return path.relative( PACKAGES_DIR, file ).split( path.sep )[ 0 ];
+}
+
+/**
+ * Returns a stream transform which maps an individual stylesheet to its
+ * package entrypoint. Unlike JavaScript which uses an external bundler to
+ * efficiently manage rebuilds by entrypoints, stylesheets are rebuilt fresh
+ * in their entirety from the build script.
+ *
+ * @return {Transform} Stream transform instance.
+ */
+function createStyleEntryTransform() {
+ const packages = new Set;
+
+ return new Transform( {
+ objectMode: true,
+ async transform( file, encoding, callback ) {
+ // Only stylesheets are subject to this transform.
+ if ( path.extname( file ) !== '.scss' ) {
+ this.push( file );
+ callback();
+ return;
+ }
+
+ // Only operate once per package, assuming entries are common.
+ const packageName = getPackageName( file );
+ if ( packages.has( packageName ) ) {
+ callback();
+ return;
+ }
+
+ packages.add( packageName );
+ const entries = await glob( path.resolve( PACKAGES_DIR, packageName, 'src/*.scss' ) );
+ entries.forEach( ( entry ) => this.push( entry ) );
+ callback();
+ },
+ } );
+}
+
+/**
+ * Returns a stream transform which maps an individual block.json to the
+ * index.js that imports it. Presently, babel resolves the import of json
+ * files by inlining them as a JavaScript primitive in the importing file.
+ * This transform ensures the importing file is rebuilt.
+ *
+ * @return {Transform} Stream transform instance.
+ */
+function createBlockJsonEntryTransform() {
+ const blocks = new Set;
+
+ return new Transform( {
+ objectMode: true,
+ async transform( file, encoding, callback ) {
+ const matches = /block-library[\/\\]src[\/\\](.*)[\/\\]block.json$/.exec( file );
+ const blockName = matches ? matches[ 1 ] : undefined;
+
+ // Only block.json files in the block-library folder are subject to this transform.
+ if ( ! blockName ) {
+ this.push( file );
+ callback();
+ return;
+ }
+
+ // Only operate once per block, assuming entries are common.
+ if ( blockName && blocks.has( blockName ) ) {
+ callback();
+ return;
+ }
+
+ blocks.add( blockName );
+ this.push( file.replace( 'block.json', 'index.js' ) );
+ callback();
+ },
+ } );
+}
+
+let onFileComplete = () => {};
+
+let stream;
+
+if ( files.length ) {
+ stream = new Readable( { encoding: 'utf8' } );
+ files.forEach( ( file ) => stream.push( file ) );
+ stream.push( null );
+ stream = stream
+ .pipe( createStyleEntryTransform() )
+ .pipe( createBlockJsonEntryTransform() );
+} else {
+ const bar = new ProgressBar( 'Build Progress: [:bar] :percent', {
+ width: 30,
+ incomplete: ' ',
+ total: 1,
+ } );
+
+ bar.tick( 0 );
+
+ stream = glob.stream( [
+ `${ PACKAGES_DIR }/*/src/**/*.js`,
+ `${ PACKAGES_DIR }/*/src/*.scss`,
+ ], {
+ ignore: [
+ `**/test/**`,
+ `**/__mocks__/**`,
+ ],
+ onlyFiles: true,
+ } );
+
+ // Pause to avoid data flow which would begin on the `data` event binding,
+ // but should wait until worker processing below.
+ //
+ // See: https://nodejs.org/api/stream.html#stream_two_reading_modes
+ stream
+ .pause()
+ .on( 'data', ( file ) => {
+ bar.total = files.push( file );
+ } );
+
+ onFileComplete = () => {
+ bar.tick();
+ };
+}
+
+const worker = workerFarm( require.resolve( './build-worker' ) );
+
+let ended = false,
+ complete = 0;
+
+// End the worker farm once the stream has ended and every file has been
+// processed. Completions must be counted even before the stream ends, since
+// workers can finish files before the glob stream emits `end`.
+const maybeEndWorkers = () => {
+ if ( ended && complete === files.length ) {
+ workerFarm.end( worker );
+ }
+};
+
+stream
+ .on( 'data', ( file ) => worker( file, ( error ) => {
+ onFileComplete();
+
+ if ( error ) {
+ // If an error occurs, the process can't be ended immediately since
+ // other workers are likely pending. Optimally, it would end at the
+ // earliest opportunity (after the current round of workers has had
+ // the chance to complete), but this is not made directly possible
+ // through `worker-farm`. Instead, ensure at least that when the
+ // process does exit, it exits with a non-zero code to reflect the
+ // fact that an error had occurred.
+ process.exitCode = 1;
+
+ console.error( error );
+ }
+
+ ++complete;
+ maybeEndWorkers();
+ } ) )
+ .on( 'end', () => {
+ ended = true;
+ maybeEndWorkers();
+ } )
+ .resume();
+
+/* eslint-enable no-console */
diff --git a/gb-src/bin/packages/get-babel-config.js b/gb-src/bin/packages/get-babel-config.js
new file mode 100644
index 0000000000000..d76e171d46b21
--- /dev/null
+++ b/gb-src/bin/packages/get-babel-config.js
@@ -0,0 +1,38 @@
+module.exports = function( environment = '', file ) {
+ /*
+ * Specific options to be passed using the caller config option:
+ * https://babeljs.io/docs/en/options#caller
+ *
+ * The caller options can only be 'boolean', 'string', or 'number' by design:
+ * https://github.com/babel/babel/blob/bd0c62dc0c30cf16a4d4ef0ddf21d386f673815c/packages/babel-core/src/config/validation/option-assertions.js#L122
+ */
+ const callerOpts = { caller: {
+ name: `WP_BUILD_${ environment.toUpperCase() }`,
+ } };
+ switch ( environment ) {
+ case 'main':
+ // to be merged as a presetEnv option
+ callerOpts.caller.modules = 'commonjs';
+ break;
+ case 'module':
+ // to be merged as a presetEnv option
+ callerOpts.caller.modules = false;
+ // to be merged as a pluginTransformRuntime option
+ callerOpts.caller.useESModules = true;
+ break;
+ default:
+ // preventing measure, this shouldn't happen ever
+ delete callerOpts.caller;
+ }
+
+ // Sourcemaps options
+ const sourceMapsOpts = {
+ sourceMaps: true,
+ sourceFileName: file,
+ };
+
+ return {
+ ...callerOpts,
+ ...sourceMapsOpts,
+ };
+};
diff --git a/gb-src/bin/packages/get-packages.js b/gb-src/bin/packages/get-packages.js
new file mode 100644
index 0000000000000..de0147435dad2
--- /dev/null
+++ b/gb-src/bin/packages/get-packages.js
@@ -0,0 +1,62 @@
+/**
+ * External dependencies
+ */
+const fs = require( 'fs' );
+const path = require( 'path' );
+const { isEmpty, overEvery } = require( 'lodash' );
+
+/**
+ * Absolute path to packages directory.
+ *
+ * @type {string}
+ */
+const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );
+
+/**
+ * Returns true if the given base file name for a file within the packages
+ * directory is itself a directory.
+ *
+ * @param {string} file Packages directory file.
+ *
+ * @return {boolean} Whether file is a directory.
+ */
+function isDirectory( file ) {
+ return fs.lstatSync( path.resolve( PACKAGES_DIR, file ) ).isDirectory();
+}
+
+/**
+ * Returns true if the given packages has "module" field.
+ *
+ * @param {string} file Packages directory file.
+ *
+ * @return {boolean} Whether file is a directory.
+ */
+function hasModuleField( file ) {
+ const { module } = require( path.resolve( PACKAGES_DIR, file, 'package.json' ) );
+
+ return ! isEmpty( module );
+}
+
+/**
+ * Filter predicate, returning true if the given base file name is to be
+ * included in the build.
+ *
+ * @param {string} pkg File base name to test.
+ *
+ * @return {boolean} Whether to include file in build.
+ */
+const filterPackages = overEvery( isDirectory, hasModuleField );
+
+/**
+ * Returns the absolute path of all WordPress packages
+ *
+ * @return {Array} Package paths
+ */
+function getPackages() {
+ return fs
+ .readdirSync( PACKAGES_DIR )
+ .filter( filterPackages )
+ .map( ( file ) => path.resolve( PACKAGES_DIR, file ) );
+}
+
+module.exports = getPackages;
diff --git a/gb-src/bin/packages/post-css-config.js b/gb-src/bin/packages/post-css-config.js
new file mode 100644
index 0000000000000..3d7861f75044b
--- /dev/null
+++ b/gb-src/bin/packages/post-css-config.js
@@ -0,0 +1,64 @@
+module.exports = [
+ require( '@wordpress/postcss-themes' )( {
+ defaults: {
+ primary: '#0085ba',
+ secondary: '#11a0d2',
+ toggle: '#11a0d2',
+ button: '#007cba',
+ outlines: '#007cba',
+ },
+ themes: {
+ 'admin-color-light': {
+ primary: '#0085ba',
+ secondary: '#c75726',
+ toggle: '#11a0d2',
+ button: '#0085ba',
+ outlines: '#007cba',
+ },
+ 'admin-color-blue': {
+ primary: '#82b4cb',
+ secondary: '#d9ab59',
+ toggle: '#82b4cb',
+ button: '#d9ab59',
+ outlines: '#417e9B',
+ },
+ 'admin-color-coffee': {
+ primary: '#c2a68c',
+ secondary: '#9fa47b',
+ toggle: '#c2a68c',
+ button: '#c2a68c',
+ outlines: '#59524c',
+ },
+ 'admin-color-ectoplasm': {
+ primary: '#a7b656',
+ secondary: '#c77430',
+ toggle: '#a7b656',
+ button: '#a7b656',
+ outlines: '#523f6d',
+ },
+ 'admin-color-midnight': {
+ primary: '#e14d43',
+ secondary: '#77a6b9',
+ toggle: '#77a6b9',
+ button: '#e14d43',
+ outlines: '#497b8d',
+ },
+ 'admin-color-ocean': {
+ primary: '#a3b9a2',
+ secondary: '#a89d8a',
+ toggle: '#a3b9a2',
+ button: '#a3b9a2',
+ outlines: '#5e7d5e',
+ },
+ 'admin-color-sunrise': {
+ primary: '#d1864a',
+ secondary: '#c8b03c',
+ toggle: '#c8b03c',
+ button: '#d1864a',
+ outlines: '#837425',
+ },
+ },
+ } ),
+ require( 'autoprefixer' )( { grid: true } ),
+ require( 'postcss-color-function' ),
+];
diff --git a/gb-src/bin/packages/watch.js b/gb-src/bin/packages/watch.js
new file mode 100644
index 0000000000000..1add26a5677a7
--- /dev/null
+++ b/gb-src/bin/packages/watch.js
@@ -0,0 +1,78 @@
+/**
+ * External dependencies
+ */
+const fs = require( 'fs' );
+const watch = require( 'node-watch' );
+const { spawn } = require( 'child_process' );
+const path = require( 'path' );
+const chalk = require( 'chalk' );
+
+/**
+ * Internal dependencies
+ */
+const getPackages = require( './get-packages' );
+
+const BUILD_SCRIPT = path.resolve( __dirname, './build.js' );
+
+let filesToBuild = new Map();
+
+const exists = ( filename ) => {
+ try {
+ return fs.statSync( filename ).isFile();
+ } catch ( e ) {}
+ return false;
+};
+
+// Exclude test files including .js files inside of __tests__ or test folders
+// and files with a suffix of .test or .spec (e.g. blocks.test.js),
+// and deceitful source-like files, such as editor swap files.
+const isSourceFile = ( filename ) => {
+ return ! [ /\/(__tests__|test)\/.+.js$/, /.\.(spec|test)\.js$/ ].some( ( regex ) => regex.test( filename ) ) && /.\.(js|json|scss)$/.test( filename );
+};
+
+const rebuild = ( filename ) => filesToBuild.set( filename, true );
+
+getPackages().forEach( ( p ) => {
+ const srcDir = path.resolve( p, 'src' );
+ try {
+ fs.accessSync( srcDir, fs.F_OK );
+ watch( path.resolve( p, 'src' ), { recursive: true }, ( event, filename ) => {
+ if ( ! isSourceFile( filename ) ) {
+ return;
+ }
+
+ const filePath = path.resolve( srcDir, filename );
+ if ( ( event === 'update' ) && exists( filePath ) ) {
+ // eslint-disable-next-line no-console
+ console.log( chalk.green( '->' ), `${ event }: ${ filename }` );
+ rebuild( filePath );
+ } else {
+ const buildFile = path.resolve( srcDir, '..', 'build', filename );
+ try {
+ fs.unlinkSync( buildFile );
+ process.stdout.write(
+ chalk.red( ' \u2022 ' ) +
+ path.relative( path.resolve( srcDir, '..', '..' ), buildFile ) +
+ ' (deleted)' +
+ '\n'
+ );
+ } catch ( e ) {}
+ }
+ } );
+ } catch ( e ) {
+ // doesn't exist
+ }
+} );
+
+setInterval( () => {
+ const files = Array.from( filesToBuild.keys() );
+ if ( files.length ) {
+ filesToBuild = new Map();
+ try {
+ spawn( 'node', [ BUILD_SCRIPT, ...files ], { stdio: [ 0, 1, 2 ] } );
+ } catch ( e ) {}
+ }
+}, 100 );
+
+// eslint-disable-next-line no-console
+console.log( chalk.red( '->' ), chalk.cyan( 'Watching for changes...' ) );
diff --git a/gb-src/bin/process-git-diff.js b/gb-src/bin/process-git-diff.js
new file mode 100644
index 0000000000000..b80b117daa59e
--- /dev/null
+++ b/gb-src/bin/process-git-diff.js
@@ -0,0 +1,34 @@
+// npm will introduce changes to a `package-lock.json` file for optional
+// dependencies varying on environment. If the only changes are the
+// addition of an "optional" flag in `package-lock.json` file from
+// `git diff`: we ignore the results.
+//
+// See: https://github.com/npm/npm/issues/17722
+
+// Example usage:
+//
+// git diff -U0 | xargs -0 node bin/process-git-diff
+
+// Example input:
+//
+// diff --git a/package-lock.json b/package-lock.json
+// index e8c8a25dc..251af8689 100644
+// --- a/package-lock.json
+// +++ b/package-lock.json
+// @@ -14373 +14373,2 @@
+// - "dev": true
+// + "dev": true,
+// + "optional": true
+// @@ -14648 +14649,2 @@
+// - "dev": true
+// + "dev": true,
+// + "optional": true
+
+const hasNonOptionalDiff = !! ( process.argv[ 2 ] || '' )
+ // Strip individual diffs of optional-only.
+ .replace( /@@ .+ @@\n(-.+\n\+.+,\n)?\+.+\"optional\": true,?\n/gm, '' )
+ // If no more line diffs remain after above, remove diff heading for file.
+ .replace( /diff --git a\/package-lock.json b\/package-lock.json\nindex \w+..\w+ \d+\n--- a\/package-lock.json\n\+\+\+ b\/package-lock.json\n(?!@@)/, '' );
+
+// Exit with error code if, after replace, changes still exist.
+process.exit( hasNonOptionalDiff ? 1 : 0 );
diff --git a/gb-src/bin/setup-local-env.sh b/gb-src/bin/setup-local-env.sh
new file mode 100755
index 0000000000000..a427fb4e3f3ef
--- /dev/null
+++ b/gb-src/bin/setup-local-env.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Exit if any command fails.
+set -e
+
+echo "Hi there! It looks like you're trying to use the old local environment setup script. This script has been retired, running \`npm run env install\` will setup a local environment for you, instead.
+
+Check out the documentation for more information: https://developer.wordpress.org/block-editor/contributors/develop/getting-started/"
diff --git a/gb-src/bin/update-readmes.js b/gb-src/bin/update-readmes.js
new file mode 100755
index 0000000000000..df420007de397
--- /dev/null
+++ b/gb-src/bin/update-readmes.js
@@ -0,0 +1,70 @@
+/**
+ * Node dependencies.
+ */
+const { join } = require( 'path' );
+const spawnSync = require( 'child_process' ).spawnSync;
+
+const packages = [
+ 'a11y',
+ 'autop',
+ 'blob',
+ 'block-editor',
+ 'block-library',
+ 'block-serialization-default-parser',
+ 'blocks',
+ 'compose',
+ [ 'core-data', {
+ 'Autogenerated actions': 'src/actions.js',
+ 'Autogenerated selectors': 'src/selectors.js',
+ } ],
+ 'data',
+ 'data-controls',
+ 'date',
+ 'deprecated',
+ 'dom',
+ 'dom-ready',
+ 'e2e-test-utils',
+ 'edit-post',
+ 'element',
+ 'escape-html',
+ 'html-entities',
+ 'i18n',
+ 'keycodes',
+ 'plugins',
+ 'priority-queue',
+ 'redux-routine',
+ 'rich-text',
+ 'shortcode',
+ 'url',
+ 'viewport',
+ 'wordcount',
+];
+
+packages.forEach( ( entry ) => {
+ if ( ! Array.isArray( entry ) ) {
+ entry = [ entry, { 'Autogenerated API docs': 'src/index.js' } ];
+ }
+
+ const [ packageName, targets ] = entry;
+
+ Object.entries( targets ).forEach( ( [ token, path ] ) => {
+ // Each target operates over the same file, so it needs to be processed synchronously,
+ // as to make sure the processes don't overwrite each other.
+ const { status, stderr } = spawnSync(
+ join( __dirname, '..', 'node_modules', '.bin', 'docgen' ),
+ [
+ join( 'packages', packageName, path ),
+ `--output packages/${ packageName }/README.md`,
+ '--to-token',
+ `--use-token "${ token }"`,
+ '--ignore "/unstable|experimental/i"',
+ ],
+ { shell: true },
+ );
+
+ if ( status !== 0 ) {
+ process.stderr.write( `${ packageName } ${ stderr.toString() }\n` );
+ process.exit( 1 );
+ }
+ } );
+} );
diff --git a/gb-src/composer.json b/gb-src/composer.json
new file mode 100644
index 0000000000000..e869eb3ccc7ff
--- /dev/null
+++ b/gb-src/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "wordpress/gutenberg",
+ "type": "wordpress-plugin",
+ "license": "GPL-2.0-or-later",
+ "description": "Prototyping since 1440. Development hub for the editor focus in core.",
+ "homepage": "https://wordpress.github.io/gutenberg/",
+ "keywords": [
+ "gutenberg", "wordpress", "editor", "wp", "react", "javascript"
+ ],
+ "support": {
+ "issues": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
+ "squizlabs/php_codesniffer": "^3.4.2",
+ "phpcompatibility/php-compatibility": "^9.2.0",
+ "wp-coding-standards/wpcs": "^2.1.1"
+ },
+ "require": {
+ "composer/installers": "~1.0"
+ },
+ "scripts": {
+ "format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source",
+ "lint": "phpcs --standard=phpcs.xml.dist"
+ }
+}
diff --git a/gb-src/composer.lock b/gb-src/composer.lock
new file mode 100644
index 0000000000000..95af33e6daad8
--- /dev/null
+++ b/gb-src/composer.lock
@@ -0,0 +1,359 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "14e6f0898af5e07bd8ff8b2bc6172712",
+ "packages": [
+ {
+ "name": "composer/installers",
+ "version": "v1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/installers.git",
+ "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/installers/zipball/cfcca6b1b60bc4974324efb5783c13dca6932b5b",
+ "reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0"
+ },
+ "replace": {
+ "roundcube/plugin-installer": "*",
+ "shama/baton": "*"
+ },
+ "require-dev": {
+ "composer/composer": "1.0.*@dev",
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Composer\\Installers\\Plugin",
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Installers\\": "src/Composer/Installers"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kyle Robinson Young",
+ "email": "kyle@dontkry.com",
+ "homepage": "https://github.com/shama"
+ }
+ ],
+ "description": "A multi-framework Composer library installer",
+ "homepage": "https://composer.github.io/installers/",
+ "keywords": [
+ "Craft",
+ "Dolibarr",
+ "Eliasis",
+ "Hurad",
+ "ImageCMS",
+ "Kanboard",
+ "Lan Management System",
+ "MODX Evo",
+ "Mautic",
+ "Maya",
+ "OXID",
+ "Plentymarkets",
+ "Porto",
+ "RadPHP",
+ "SMF",
+ "Thelia",
+ "WolfCMS",
+ "agl",
+ "aimeos",
+ "annotatecms",
+ "attogram",
+ "bitrix",
+ "cakephp",
+ "chef",
+ "cockpit",
+ "codeigniter",
+ "concrete5",
+ "croogo",
+ "dokuwiki",
+ "drupal",
+ "eZ Platform",
+ "elgg",
+ "expressionengine",
+ "fuelphp",
+ "grav",
+ "installer",
+ "itop",
+ "joomla",
+ "kohana",
+ "laravel",
+ "lavalite",
+ "lithium",
+ "magento",
+ "majima",
+ "mako",
+ "mediawiki",
+ "modulework",
+ "modx",
+ "moodle",
+ "osclass",
+ "phpbb",
+ "piwik",
+ "ppi",
+ "puppet",
+ "pxcms",
+ "reindex",
+ "roundcube",
+ "shopware",
+ "silverstripe",
+ "sydes",
+ "symfony",
+ "typo3",
+ "wordpress",
+ "yawik",
+ "zend",
+ "zikula"
+ ],
+ "time": "2018-08-27T06:10:37+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
+ "reference": "e749410375ff6fb7a040a68878c656c2e610b132"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e749410375ff6fb7a040a68878c656c2e610b132",
+ "reference": "e749410375ff6fb7a040a68878c656c2e610b132",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0",
+ "php": "^5.3|^7",
+ "squizlabs/php_codesniffer": "^2|^3"
+ },
+ "require-dev": {
+ "composer/composer": "*",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "sensiolabs/security-checker": "^4.1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "franck.nijhof@dealerdirect.com",
+ "homepage": "http://www.frenck.nl",
+ "role": "Developer / IT Manager"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "homepage": "http://www.dealerdirect.com",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "time": "2018-10-26T13:21:45+00:00"
+ },
+ {
+ "name": "phpcompatibility/php-compatibility",
+ "version": "9.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
+ "reference": "3db1bf1e28123fd574a4ae2e9a84072826d51b5e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/3db1bf1e28123fd574a4ae2e9a84072826d51b5e",
+ "reference": "3db1bf1e28123fd574a4ae2e9a84072826d51b5e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3",
+ "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
+ },
+ "conflict": {
+ "squizlabs/php_codesniffer": "2.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
+ "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
+ },
+ {
+ "name": "Wim Godden",
+ "homepage": "https://github.com/wimg",
+ "role": "lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ }
+ ],
+ "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
+ "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
+ "keywords": [
+ "compatibility",
+ "phpcs",
+ "standards"
+ ],
+ "time": "2019-06-27T19:58:56+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.4.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+ "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2019-04-10T23:49:02+00:00"
+ },
+ {
+ "name": "wp-coding-standards/wpcs",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
+ "reference": "bd9c33152115e6741e3510ff7189605b35167908"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/bd9c33152115e6741e3510ff7189605b35167908",
+ "reference": "bd9c33152115e6741e3510ff7189605b35167908",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.3.1"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "suggest": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "wordpress"
+ ],
+ "time": "2019-05-21T02:50:00+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/gb-src/docs/contributors/coding-guidelines.md b/gb-src/docs/contributors/coding-guidelines.md
new file mode 100644
index 0000000000000..2e02dfeaa10f7
--- /dev/null
+++ b/gb-src/docs/contributors/coding-guidelines.md
@@ -0,0 +1,211 @@
+# Coding Guidelines
+
+This living document serves to prescribe coding guidelines specific to the Gutenberg project. Base coding guidelines follow the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/). The following sections outline additional patterns and conventions used in the Gutenberg project.
+
+## CSS
+
+### Naming
+
+To avoid class name collisions, class names **must** adhere to the following guidelines, which are loosely inspired by the [BEM (Block, Element, Modifier) methodology](https://en.bem.info/methodology/).
+
+All class names assigned to an element must be prefixed with the name of the package, followed by a dash and the name of the directory in which the component resides. Any descendent of the component's root element must append a dash-delimited descriptor, separated from the base by two consecutive underscores `__`.
+
+* Root element: `package-directory`
+* Child elements: `package-directory__descriptor-foo-bar`
+
+The root element is considered to be the highest ancestor element returned by the default export in the `index.js`. Notably, if your folder contains multiple files, each with their own default exported component, only the element rendered by that of `index.js` can be considered the root. All others should be treated as descendents.
+
+**Example:**
+
+Consider the following component located at `packages/components/src/notice/index.js`:
+
+```jsx
+export default function Notice( { children, onRemove } ) {
+ return (
+
+
+ { children }
+
+
+
+ );
+}
+```
+
+Components may be assigned with class names that indicate states (for example, an "active" tab or an "opened" panel). These modifiers should be applied as a separate class name, prefixed as an adjective expression by `is-` (`is-active` or `is-opened`). In rare cases, you may encounter variations of the modifier prefix, usually to improve readability (`has-warning`). Because a modifier class name is not contextualized to a specific component, it should always be written in stylesheets as accompanying the component being modified (`.components-panel.is-opened`).
+
+**Example:**
+
+Consider again the Notices example. We may want to apply specific styling for dismissible notices. The [`classnames` package](https://www.npmjs.com/package/classnames) can be a helpful utility for conditionally applying modifier class names.
+
+```jsx
+import classnames from 'classnames';
+
+export default function Notice( { children, onRemove, isDismissible } ) {
+ const classes = classnames( 'components-notice', {
+ 'is-dismissible': isDismissible,
+ } );
+
+ return (
+
+ { /* ... */ }
+
+ );
+}
+```
+
+A component's class name should **never** be used outside its own folder (with rare exceptions such as [`_z-index.scss`](https://github.com/WordPress/gutenberg/blob/master/assets/stylesheets/_z-index.scss)). If you need to inherit styles of another component in your own components, you should render an instance of that other component. At worst, you should duplicate the styles within your own component's stylesheet. This is intended to improve maintainability by treating individual components as the isolated abstract interface.
+
+#### SCSS File Naming Conventions for Blocks
+
+The build process will split SCSS from within the blocks library directory into two separate CSS files when Webpack runs.
+
+Styles placed in a `style.scss` file will be built into `blocks/build/style.css`, to load on the front end theme as well as in the editor. If you need additional styles specific to the block's display in the editor, add them to an `editor.scss`.
+
+Examples of styles that appear in both the theme and the editor include gallery columns and drop caps.
+
+## JavaScript
+
+### Imports
+
+In the Gutenberg project, we use [the ES2015 import syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) to enable us to create modular code with clear separations between code of a specific feature, code shared across distinct WordPress features, and third-party dependencies.
+
+These separations are identified by multi-line comments at the top of a file which imports code from another file or source.
+
+#### External Dependencies
+
+An external dependency is third-party code that is not maintained by WordPress contributors, but instead [included in WordPress as a default script](https://developer.wordpress.org/reference/functions/wp_enqueue_script/#default-scripts-included-and-registered-by-wordpress) or referenced from an outside package manager like [npm](https://www.npmjs.com/).
+
+Example:
+
+```js
+/**
+ * External dependencies
+ */
+import moment from 'moment';
+```
+
+#### WordPress Dependencies
+
+To encourage reusability between features, our JavaScript is split into domain-specific modules which [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) one or more functions or objects. In the Gutenberg project, we've distinguished these modules under top-level directories. Each module serve an independent purpose, and often code is shared between them. For example, in order to localize its text, editor code will need to include functions from the `i18n` module.
+
+Example:
+
+```js
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+```
+
+#### Internal Dependencies
+
+Within a specific feature, code is organized into separate files and folders. As is the case with external and WordPress dependencies, you can bring this code into scope by using the `import` keyword. The main distinction here is that when importing internal files, you should use relative paths specific to top-level directory you're working in.
+
+Example:
+
+```js
+/**
+ * Internal dependencies
+ */
+import VisualEditor from '../visual-editor';
+```
+
+### Experimental and Unstable APIs
+
+Experimental and unstable APIs are temporary values exported from a module whose existence is either pending future revision or provides an immediate means to an end.
+
+_To External Consumers:_
+
+**There is no support commitment for experimental and unstable APIs.** They can and will be removed or changed without advance warning, including as part of a minor or patch release. As an external consumer, you should avoid these APIs.
+
+_To Project Contributors:_
+
+An experimental or unstable API is named as such to communicate instability of a function whose interface is not yet finalized. Aside from references within the code, these APIs should neither be documented nor mentioned in any CHANGELOG. They should effectively be considered to not exist from an external perspective. In most cases, they should only be exposed to satisfy requirements between packages maintained in this repository.
+
+An experimental or unstable function or object should be prefixed respectively using `__experimental` or `__unstable`.
+
+```js
+export { __experimentalDoExcitingExperimentalAction } from './api';
+export { __unstableDoTerribleAwfulAction } from './api';
+```
+
+- An **experimental API** is one which is planned for eventual public availability, but is subject to further experimentation, testing, and discussion.
+- An **unstable API** is one which serves as a means to an end. It is not desired to ever be converted into a public API.
+
+In both cases, the API should be made stable or removed at the earliest opportunity.
+
+While an experimental API may often stabilize into a publicly-available API, there is no guarantee that it will. The conversion to a stable API will inherently be considered a breaking change by the mere fact that the function name must be changed to remove the `__experimental` prefix.
+
+### Objects
+
+When possible, use [shorthand notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_2015) when defining object property values:
+
+```js
+const a = 10;
+
+// Bad:
+const object = {
+ a: a,
+ performAction: function() {
+ // ...
+ },
+};
+
+// Good:
+const object = {
+ a,
+ performAction() {
+ // ...
+ },
+};
+```
+
+### Strings
+
+String literals should be declared with single-quotes *unless* the string itself contains a single-quote that would need to be escaped–in that case: use a double-quote. If the string contains a single-quote *and* a double-quote, you can use ES6 template strings to avoid escaping the quotes.
+
+**Note:** The single-quote character (`'`) should never be used in place of an apostrophe (`’`) for words like `it’s` or `haven’t` in user-facing strings. For test code it's still encouraged to use a real apostrophe.
+
+In general, avoid backslash-escaping quotes:
+
+```js
+// Bad:
+const name = "Matt";
+// Good:
+const name = 'Matt';
+
+// Bad:
+const pet = 'Matt\'s dog';
+// Also bad (not using an apostrophe):
+const pet = "Matt's dog";
+// Good:
+const pet = 'Matt’s dog';
+// Also good:
+const oddString = "She said 'This is odd.'";
+```
+
+You should use ES6 Template Strings over string concatenation whenever possible:
+
+```js
+const name = 'Stacey';
+
+// Bad:
+alert( 'My name is ' + name + '.' );
+// Good:
+alert( `My name is ${ name }.` );
+```
+
+## PHP
+
+We use
+[`phpcs` (PHP\_CodeSniffer)](https://github.com/squizlabs/PHP_CodeSniffer) with the [WordPress Coding Standards ruleset](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) to run a lot of automated checks against all PHP code in this project. This ensures that we are consistent with WordPress PHP coding standards.
+
+The easiest way to use PHPCS is [local environment](/docs/contributors/getting-started.md#local-environment). Once that's installed, you can check your PHP by running `npm run lint-php`.
+
+If you prefer to install PHPCS locally, you should use `composer`. [Install `composer`](https://getcomposer.org/download/) on your computer, then run `composer install`. This will install `phpcs` and `WordPress-Coding-Standards` which you can then run via `composer lint`.
diff --git a/gb-src/docs/contributors/copy-guide.md b/gb-src/docs/contributors/copy-guide.md
new file mode 100644
index 0000000000000..a3178b69109b3
--- /dev/null
+++ b/gb-src/docs/contributors/copy-guide.md
@@ -0,0 +1,284 @@
+# Copy Guidelines
+
+## Longer Text
+Guidelines for writing multi-line/step instructions or narrative introductions/orientation to pages or features.
+
+This will obviously vary quite a lot depending on the context, but here are some general tips:
+
+#### ONE: Contractions are your friends!
+They’re more conversational, and a simple way to make text sound friendlier and less formal. (And they save a bit of space as well: a win-win.)
+
+#### TWO: Cut phrases that inflate your word count without actually adding meaning.
+This happens frequently in two specific instances. First, when writing in the passive voice:
+
+> This block can be used to display single images.
+
+Any time you see phrases like “can be” or “is used”: halt. You’re writing in the passive voice. Try going active for a snappier (and shorter) sentence:
+
+> This block displays single images.
+
+Second, this happens when we hedge instead of making declarative statements:
+
+> The gallery block can help you display multiple images in an elegant layout.
+
+Does it or doesn’t it? We’re making this software: we’re allowed to be declarative about what it is and does:
+
+> The gallery block displays multiple images in an elegant layout.
+
+We also all do this a lot with the phrase “allows you to.”
+
+> Preformatted text allows you to keep your tabs and line breaks.
+
+Features don’t allow anyone to do anything; they’re just tools that do specific things to achieve an end. Just say what they do:
+
+> Preformatted text preserves your tabs and line breaks.
+
+The more direct sentences are almost always clearer. Scan your copy for the words “can,” “be,” “might,” “allows you to,” and “helps”—they’re the most common culprits, and looking for those words specifically is a way to locate phrasing you can tighten up.
+
+#### THREE: Beware of “simple,” “easy,” and “just.”
+It is not for us to decide what is simple: it’s for the user to decide. If we say something is easy and the user doesn’t have an easy experience, it undermines their trust in us and what we’re building. Same goes for “just”—many of us know to avoid “simple,” but still use “just” all the time. “Just click here.” “Just enter your username.” It’s the same thing: it implies that something will be no big deal, but we can’t know what the user will find to be a big deal.
+
+It’s also safer and more helpful to be specific. “Easy” and “simple” are shorthand for explanations that we haven’t written; whenever you see them, take a minute to think about what they’re standing in for. Maybe “It’s easy to add a block by hitting ‘enter’” really means “You can add more content to the page without taking your hands off the keyboard.” Great! Say the specific thing instead of relying on “easy.”
+
+This isn’t to say that you should banish these words from your vocabulary. You might want to write a tooltip describing how the cover image block now requires less configuration, or an email about how we’re building a tool for quick creation of custom blocks, and you could legitimately say that the cover image block has been simplified or that we’re working to make custom block creation easier—there, the terms are descriptive and relative. But be on the lookout for ways you might be using (or overusing) them to make absolute claims that something is easy or simple, and use those as opportunities to be more specific and clear.
+
+#### FOUR: Look out for “we.”
+Any time text or instructions uses “we” a lot, it means the focus of the text is on the people behind the software and not the people using the software. Sometimes that’s what you actually want—but it’s usually not. The focus should typically be on the user, what they need, and how they benefit rather than “what we did” or “what we want.”
+
+We’re the only ones that care about what we did or want; the user just wants software that works. If you see a lot of “we”s, think about whether you should reframe what you’re writing to focus on the benefits to and successes of the user.
+
+## Bulleted Lists
+Guidelines for (duh) writing bulleted lists.
+
+#### ONE: Keep sentence structures parallel across all bullets.
+Parallel structure makes lists easier to read quickly—their predictability takes some cognitive load off the reader.
+
+GOOD:
+> What can you do with this block? Lots of things!
+> * Add a quote.
+> * Highlight a link.
+> * Display multiple images.
+> * Create a bulleted list.
+
+Every bullet is a full sentence, and ends with a period. (If your list is a bunch of one- or two-word items, those can often just turn into a single regular sentence—easier to read, and space-saving.) Every line begins with a verb that tells the user what the block can do. The subject of the sentence is always the user.
+
+A user can absorb this list quickly because once they read the first item, they understand how to read the rest and know what information they’ll find.
+
+LESS GOOD:
+> What can you do with this block? Lots of things!
+> * You can add a quote.
+> * Highlighting a link you love.
+> * It displays multiple images. Nice for galleries!
+> * Bulleted lists
+
+Here, every line has different phrasing (some start with a verb, some with a noun) and the subject of the sentence changes (sometimes it’s you, sometimes it’s the block). Some lines have added description, some don't. There’s an incomplete sentence, and punctuation is inconsistent.
+
+Reading this list takes more work because the reader has to parse each bullet anew. They can’t assume each bullet will contain similar information.
+
+Note: this doesn't mean every bullet has to be super short and start with an action verb! “Predictable” doesn’t have to mean “simple.” It just means that each bullet should have the same sentence structure. This list would also be fine:
+
+> What can you do with this block? Lots of things!
+> * Try adding a quote. Sometimes someone else said things best!
+> * Use it to highlight a link you love—sharing links is the currency of the internet.
+> * Create a gallery that displays multiple images, and show off your best photos.
+
+Here, each bullet starts with a more user-focused verb and includes a piece of supplemental information for more interest. The punctuation varies a bit, which keeps the lines from feeling too formulaic, but since the basic structure of each is the same, they remain easy to read.
+
+#### TWO: When in doubt, start with a verb. (But not always the same verb.)
+Do you have to start with a verb? No. But if you’re at a loss, you usually can’t go wrong with a verb (especially since bulleted lists are often describing a series of actions or possible actions).
+
+In a simple list that’s meant to be purely instructional (e.g., in UI copy where you just need the user to make a decision), it might be fine to start every bullet with the same verb:
+
+> To continue, choose an action:
+> * Add a simple text block.
+> * Add a pullquote block.
+> * Add an image block.
+
+If your list is more persuasive (e.g., trying to convince someone to use a feature by listing its benefits) or includes multi-step instructions, you’ll want to vary your verbs to keep the reader engaged with more interesting language, as in the example above:
+
+>What can you do with this block? Lots of things!
+>* Try adding a quote. Sometimes someone else said things best!
+>* Use it to highlight a link you love—sharing links is the currency of the internet.
+>* Create a gallery that displays multiple images, and show off your best photos.
+
+These aren’t hard-and-fast rules—you might choose the use the same verb in a persuasive list to be more focused and powerful, for example. But they’re good starting places for solid lists.
+
+#### THREE: When something's clearly a list, you don't have to tell us it's a list.
+
+GOOD:
+> What can you do with this block? Lots of things!
+> * Add a quote.
+> * Highlight a link you love.
+> * Display multiple images.
+
+LESS GOOD:
+> What can you do with this block? Lots of things! Here are some examples of ways you can use it.
+> * You can add a quote.
+> * Highlighting a link you love.
+> * It displays multiple images. Nice for galleries!
+
+Find the balance between being as clear as possible and trusting a user. On one hand, we know that people don’t always read instructions; on the other, redundancy can make the user feel like we think they’re stupid.
+
+#### FOUR: Bold is sometimes your friend.
+Use it to focus readers on the key information in a bulleted list. This is especially useful when your bullets include some supplemental but ultimately secondary information.
+
+“Key information” is, well, key: bold draws the eye, so stick to the most vital piece of information in a given bullet:
+
+>What can you do with this block? Lots of things!
+> * Try adding a **quote**. Sometimes someone else said things best!
+> * Use it to highlight a **link** you love—sharing links is the currency of the internet.
+> * Create a **gallery** that displays multiple images, and show off your best photos.
+
+On the flipside, bolding too many things creates visual confusion:
+
+> **What can you do with this block?** Lots of things!
+> * Try adding a **quote**. Sometimes someone else said things best!
+> * Use it to highlight a **link** you love—sharing **links** is the currency of the internet.
+> * Create a **gallery** that displays **multiple images**, and show off your best **photos**.
+
+When lists are short and basic, don't bother—bolding just adds busy-ness.
+
+> What can you do with this block? Lots of things!
+> * Add a **quote**.
+> * Highlight a **link**.
+> * Display multiple **images**.
+
+The lack of words creates its own focus; you don't have to add any more.
+
+## UI Descriptions
+Guidelines for writing one-line feature descriptions, or short descriptions to clarify options.
+
+#### ONE: Clarity above all!
+If the user doesn't understand what using a particular option will result in, it doesn't matter how clever your pun is. Wordplay and idioms are frequently unclear, and easily misunderstood. If you use them at all, they should be as supplemental information— never to explain the main idea—and they should be something you’re fairly certain will be understandable to a pretty wide range of people.
+
+#### TWO: Refer back to section one, and look out for those bulk-adding phrases.
+Active voice is typically the better way to go, and cutting out the bulky phrasing is particularly important when you’ve got limited space and you need people to be able to make decisions and act. Often you can shorten a UI instruction phrase to be both shorter and clearer:
+
+> When you click X, Y happens.
+
+vs.
+
+> Click X to do Y.
+
+While it can feel like adding the extra words helps walk a user through the product, the extra words just serve to obscure the point being communicated:
+
+> When you click the “settings” button, the pop-up will display the advanced settings that are available.
+
+vs.
+
+> Click “settings” to access the advanced settings.
+
+Similar phrases are “Once you do X…” or “If you want to do X…” Sometimes there are decision points where “If you want to do X…” is entirely appropriate because there are different paths the user can take based on their goal. But, we often use it to mean “Here is a thing you can do,” which you can express more simply as: “To do X…”
+
+#### THREE: Be specific.
+When an action depends on the user having completed some prior action, be specific about what’s required and what happens next. We often default to “when you’re ready.”
+Ready for what? Be specific about whatever the prerequisites are.
+
+“When you’re ready” can mean:
+
+* When you want to add another block”
+* When you’re satisfied with your post”
+* After you’ve finished proofreading your post”
+* When you’d like to add a featured image”
+* After you’ve configured all the settings”
+
+And when something means everything, it actually means nothing. The more specific instructions are, the more useful they are, and the more trust the person following them will have in the product.
+
+#### FOUR: This is still writing. It should have personality and interest.
+Clarity above all, yes, and space is often limited here—but UI text can still be interesting to read.
+
+Single lines of description can still be complete sentences.
+
+> List. Numbered or bulleted.
+
+vs.
+
+> Add a list, either numbered or bulleted.
+
+You can still use contractions.
+
+> Add a list. We will provide formatting options.
+
+vs.
+
+> Add a bulleted list—we’ll give you some formatting options.
+
+You can still use punctuation—em dashes, colons, semicolons—to control the flow of your words, link ideas, and create pauses.
+
+> List. Numbered or bulleted.
+
+vs.
+
+> Add a list—numbered or bulleted. Your choice!
+
+You can still try to avoid jargon in favor of plain language.
+
+> Add unordered or ordered list.
+
+vs.
+
+> Add a list, either numbered or bulleted.
+
+(And because it bears repeating: no wordplay, please! “Personality” can—and in UI instructions, should—be subtle. We’re talking about text that sounds like it was said by a human being, not forced attempts at whimsy.)
+
+#### FIVE: Pay attention to capitalization.
+When it comes to headlines and subheads, there are two ways to capitalize:
+
+In Title Case, the First Letter of Almost Every Word Is Capitalized
+
+In sentence case, only the first letter of the line is capitalized
+
+Feature names and dashboard sections typically use title case (think “Site Stats” or “Recently Published”), whereas feature labels typically use sentence case (like “Show buttons on” or “Comment Likes are,” where “Likes” is capitalized because it’s the feature name, but the overall label is using sentence case).
+
+When you’re looking at a full page of UI copy, make sure you’re being consistent across all of it, and that all similar kinds of copy—headlines, tooltips, buttons, etc.—are using the same case.
+
+## Error Messaging
+Guidelines for writing error messages that are understandable and useful.
+
+#### ONE: Don’t ignore voice/tone in error messaging—they communicate a lot.
+Voice and tone can say as much as the individual words themselves. Error messages have to convey a significant amount of information and usually need to be fairly short, but try not to sacrifice tone, or to go too far in either a negative or positive direction.
+
+Let’s say someone’s trying to publish a post, but their user role doesn’t allow them to do that. Here are some ways we could—but should not—communicate that:
+
+> Your user role is incorrect.
+
+Here, we sound distant and uncaring.
+
+> Stop! You do not have permission to do this.
+
+Here, we sound unnecessarily alarmist and stern.
+
+> Oopsie, we can’t let you do that!
+
+Here, we sound too cute.
+
+We can stay direct, positive, and friendly, even in error messages. How? With tips two through four!
+
+#### TWO: Whenever possible, offer a path to resolution.
+A good error message doesn’t just alert someone to the fact that something is wrong.
+
+> Your user role is incorrect.
+
+Okay, fine. Why does that matter? What do I do about it? How does this message help me? I need to know why my user role matters, and how to get the role I need so I can complete the action I want to complete. An error message that doesn’t provide any instruction leaves the user without a path forward; they can’t avoid repeating the action that led to the error if we don’t tell them now.
+
+#### THREE: Don’t lean on jargon to cut down on words when space is tight.
+
+> Your user role is incorrect. Contact a site administrator.
+
+Maybe we’re getting somewhere here: now I know there’s something I can do about things, which is good.
+
+Then again, maybe we’re not: I still don’t know what my role is, or why it matters. Also, now I’m also not sure what a site administrator is, who mine is, or how to contact them.
+
+All the information in this error message is technically entirely correct, but that doesn’t mean it communicates anything useful. If the goal is understanding and resolution, technical accuracy doesn’t always get us there.
+
+“Your account does not have permission to publish posts” doesn’t use the language of the user roles UI, but it does explain what’s gone wrong and I can understand it even if I don’t know what a user role is. And since I understand, I’m also better placed to understand the resolution, even if the message ended here: I can see that I need to get permission.
+
+Consistency with existing UI language is great, but not when it gets in the way of understanding.
+
+#### FOUR: Don’t assume people understand where the error came from.
+
+> Your user role is incorrect.
+
+It might seem obvious to us that the user got this message when they tried to publish something or change a setting that they don’t have permission for. It might not be so obvious to the user: people click around a lot, especially when we’re unsure how to do something, and we don’t always remember what page or setting we were just looking at (or why!).
+
+A good error message also includes some context that orients the user. “Your account does not have permission to publish posts” reminds them that they were trying to publish a post, and that that’s the particular stumbling block that caused the error.
diff --git a/gb-src/docs/contributors/design.md b/gb-src/docs/contributors/design.md
new file mode 100644
index 0000000000000..d71221003d378
--- /dev/null
+++ b/gb-src/docs/contributors/design.md
@@ -0,0 +1,66 @@
+# Design Principles & Vision
+
+This is a living document that outlines the design principles and patterns of the editor interface. Its aim is to explain the background of the design, inform future improvements, and help people design great blocks.
+
+## Principles
+
+
+
+### Goal of Gutenberg
+
+Gutenberg's all-encompassing goal is a post- and page-building experience that makes it easy to create rich layouts. The block editor was the first product launched following this methodology for working with content.
+
+From the [kickoff post](https://make.wordpress.org/core/2017/01/04/focus-tech-and-design-leads/):
+
+> The editor will endeavour to create a new page and post building experience that makes writing rich posts effortless, and has “blocks” to make easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.
+
+We can extract a few key principles from this:
+
+- **Authoring rich posts is a key strength of WordPress.**
+- **Blocks will unify features and types of interaction under a single interface.** Users shouldn’t have to write shortcodes, custom HTML, or paste URLs to embed. Users only need to learn how the block works in order to use all of its features.
+- **Make core features more discoverable**, reducing hard-to-find “Mystery meat.” WordPress supports a large number of blocks and 30+ embeds. Let’s increase their visibility.
+
+### Why
+
+One thing that sets WordPress apart from other systems is that it allows users to create as rich a post layout as they can imagine — as long as they know HTML and CSS and build a custom theme.
+
+Gutenberg reshapes the editor into a tool that allows users write rich posts and build beautiful layouts in a few clicks — no technical knowledge needed. WordPress will become a powerful and flexible content tool that’s accessible to all.
+
+### Vision
+
+Gutenberg wants to make it easier to author rich content. This means ensuring good defaults, bundling advanced layout options into blocks, and making the most important actions immediately available. Authoring content with WordPress should be accessible to anyone.
+
+**Everything on a WordPress website becomes a block:** text, images, galleries, widgets, shortcodes, and even chunks of custom HTML, whether added by plugins or otherwise. Users will only have to learn a single interface —— the block interface.
+
+**All blocks are created equal.** They all live in the same inserter interface. Recency, search, tabs, and grouping ensure that the most-used blocks are within easy reach.
+
+**Drag-and-drop is secondary.** For greater accessibility and platform compatibility, drag-and-drop interactions are used as an additive enhancement on top of explicit actions like click, tab, and space.
+
+**Placeholders are key.** If a block can have a neutral placeholder state, it should. An image placeholder block shows a button to open the media library, and a text placeholder block shows a writing prompt. By embracing placeholders we can predefine editable layouts, so all users have to do is fill in the blanks.
+
+**Direct manipulation is intuitive.** The block interface allows users to manipulate content directly on the page. Plugin and theme authors will support and extend this experience by building their own custom blocks.
+
+**Code editing shouldn't be necessary for customization.** Customizing traditionally required complicated markup, and complicated markup is easy to break. With Gutenberg, customizing becomes more intuitive — and safer. A developer will be able to provide custom blocks that directly render portions of a layout (a three column grid of features, for instance) and clearly specify what can be directly edited by the user. That means the user can update text, swap images, reduce the number of columns, without having to ask a developer, or worrying about breaking things.
+
+### Future Opportunities
+
+The initial phase of Gutenberg as described in the kickoff goal is primarily limited to the content area (specifically `post_content`) of posts and pages. Within those confines, we are embracing the web as a vertical river of content by appending blocks sequentially, then adding layout options to each block.
+
+That said, there isn’t any fixed limit to the kind of layouts Gutenberg will be able to create. It’s very possible for Gutenberg to grow beyond the confines of post and page content, to include the whole page — one could think of a theme template as a comma-separated list of blocks, like this:
+
+```js
+{
+ 'theme/header',
+ 'theme/sidebar',
+ 'core/content' {
+ 'core/cover-image',
+ 'theme/author-card',
+ 'core/text',
+ },
+ 'theme/footer',
+}
+```
+
+Every block nested inside the content block would be _rearrangeable_. Every block would be _editable_. Every block would use the same API, and both the editor and the theme would load the same `style.css` file directly. In the end, both the editor/page builder and theme/front-end would appear near-identical, allowing for a true WYSIWYG experience.
+
+This concept is speculative, but it’s one direction Gutenberg could go in the future.
diff --git a/gb-src/docs/contributors/develop.md b/gb-src/docs/contributors/develop.md
new file mode 100644
index 0000000000000..7b8ad300ee316
--- /dev/null
+++ b/gb-src/docs/contributors/develop.md
@@ -0,0 +1,15 @@
+# Developer Contributions
+
+Please also see [CONTRIBUTING.md](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md) for general information about contributions to the Gutenberg repository.
+
+The following resources offer additional information for developers who wish to contribute to Gutenberg:
+
+* [Getting Started](/docs/contributors/getting-started.md).
+* [Git Workflow](/docs/contributors/git-workflow.md).
+* [Coding Guidelines](/docs/contributors/coding-guidelines.md) outline additional patterns and conventions used in the Gutenberg project.
+* [Testing Overview](/docs/contributors/testing-overview.md) for PHP and JavaScript development in Gutenberg.
+* [Gutenberg Block Grammar](/docs/contributors/grammar.md).
+* [Scripts](/docs/contributors/scripts.md) - a list of vendor and internal scripts available to plugin developers.
+* [Managing Packages](/docs/contributors/managing-packages.md).
+* [Gutenberg Release Process](/docs/contributors/release.md) - a checklist for the different type of releases for Gutenberg project.
+* [Localizing Gutenberg Plugin](/docs/contributors/localizing.md) - a guide on how to translate Gutenberg in your locale or language.
diff --git a/gb-src/docs/contributors/document.md b/gb-src/docs/contributors/document.md
new file mode 100644
index 0000000000000..43efc49c3db17
--- /dev/null
+++ b/gb-src/docs/contributors/document.md
@@ -0,0 +1,34 @@
+# Documentation Contributions
+
+Documentation for Gutenberg is maintained in the `/docs/` directory in the same Gutenberg Github repository. The docs are published every 15 minutes to the [Block Editor Handbook site](https://developer.wordpress.org/block-editor/).
+
+## New Document
+
+To add a new documentation page:
+
+1. Create a Markdown file in the [docs](https://github.com/WordPress/gutenberg/tree/master/docs) folder
+2. Add item to the [toc.json](https://github.com/WordPress/gutenberg/blob/master/docs/toc.json) hierarchy
+3. Update manifest.json by running `npm run docs:build`
+4. Commit manifest.json with other files updated
+
+## Using Links
+
+It's very likely that at some point you will want to link to other documentation pages. It's worth emphasizing that all documents can be browsed in different contexts:
+
+- Block Editor Handbook
+- GitHub website
+- npm website
+
+To create links that work in all contexts, you should use absolute path links without the `https://github.com/WordPress/gutenberg` prefix. You can reference files using the following patterns:
+
+- `/docs/*.md`
+- `/packages/*/README.md`
+- `/packages/components/src/**/README.md`
+
+This way they will be properly handled in all three aforementioned contexts.
+
+## Resources
+
+* [Copy Guidelines](/docs/contributors/copy-guide.md) for writing instructions, documentations, or other contributions to Gutenberg project.
+
+* [Tone and Voice Guide](https://make.wordpress.org/docs/handbook/documentation-team-handbook/tone-and-voice-guide/) from WordPress Documentation.
diff --git a/gb-src/docs/contributors/getting-started.md b/gb-src/docs/contributors/getting-started.md
new file mode 100644
index 0000000000000..3105b6a0aa15d
--- /dev/null
+++ b/gb-src/docs/contributors/getting-started.md
@@ -0,0 +1,70 @@
+# Getting Started
+
+Gutenberg is a Node.js-based project, built primarily in JavaScript.
+
+The first step is to install a recent version of Node. The easiest way (on MacOS, Linux, or Windows 10 with the Linux Subsystem) is by installing and running [nvm]. Once `nvm` is installed, you can install the correct version of Node by running `nvm install` in the Gutenberg directory.
+
+Once you have Node installed, run these scripts:
+
+```
+npm install
+npm run build
+```
+
+This will build Gutenberg, ready to be used as a WordPress plugin!
+
+If you don't have a local WordPress environment to load Gutenberg in, we can help get that up and running, too.
+
+## Local Environment
+
+The quickest way to get up and running is to use the provided Docker setup. If you don't already have it, you'll need to install Docker by following their instructions for [Windows 10 Pro](https://docs.docker.com/docker-for-windows/install/), [all other version of Windows](https://docs.docker.com/toolbox/toolbox_install_windows/), [macOS](https://docs.docker.com/docker-for-mac/install/), or [Linux](https://docs.docker.com/v17.12/install/linux/docker-ce/ubuntu/#install-using-the-convenience-script).
+
+Once Docker is installed and running, run this script to install WordPress, and build your local environment:
+
+```
+npm run env install
+```
+
+WordPress will be installed in the `wordpress` directory, if you need to access WordPress core files directly, you can find them there.
+
+If you already have WordPress checked out in a different directory, you can use that installation, instead, by running these commands:
+
+```
+export WP_DEVELOP_DIR=/path/to/wordpress-develop
+npm run env connect
+```
+
+This will use WordPress' own local environment, and mount your Gutenberg directory as a volume there.
+
+In Windows, you can set the `WP_DEVELOP_DIR` environment variable using the appropriate method for your shell:
+
+ CMD: set WP_DEVELOP_DIR=/path/to/wordpress-develop
+ PowerShell: $env:WP_DEVELOP_DIR = "/path/to/wordpress-develop"
+
+The WordPress installation should be available at `http://localhost:8889` (**Username**: `admin`, **Password**: `password`).
+If this port is in use, you can override it using the `LOCAL_PORT` environment variable. For example, `export LOCAL_PORT=7777` will change the URL to `http://localhost:7777` . If you're running [e2e tests](/docs/contributors/testing-overview.md#end-to-end-testing), this change will be used correctly.
+
+To bring down this local WordPress instance later run `npm run env stop`. To bring it back up again, run `npm run env start`.
+
+WordPress comes with specific [debug systems](https://wordpress.org/support/article/debugging-in-wordpress/) designed to simplify the process as well as standardize code across core, plugins and themes. It is possible to use environment variables (`LOCAL_WP_DEBUG` and `LOCAL_SCRIPT_DEBUG`) to update a site's configuration constants located in `wp-config.php` file. Both flags can be disabled at any time by running the following command:
+```
+LOCAL_SCRIPT_DEBUG=false LOCAL_WP_DEBUG=false npm run env install
+```
+By default, both flags will be set to `true`.
+
+## On A Remote Server
+
+Open a terminal (or if on Windows, a command prompt) and navigate to the repository you cloned. Now type `npm install` to get the dependencies all set up. Once that finishes, you can type `npm run build`. You can now upload the entire repository to your `wp-content/plugins` directory on your web server and activate the plugin from the WordPress admin.
+
+You can also type `npm run package-plugin` which will run the two commands above and create a zip file automatically for you which you can use to install Gutenberg through the WordPress admin.
+
+[npm]: https://www.npmjs.com/
+[nvm]: https://github.com/creationix/nvm
+
+## Playground
+
+The Gutenberg repository also includes a static Gutenberg playground that allows testing and developing in a WordPress-agnostic context. This is very helpful for developing reusable components and trying generic JavaScript modules without any backend dependency.
+
+You can launch the playground by running `npm run playground:start` locally. The playground should be available on [http://localhost:1234](http://localhost:1234).
+
+You can also test the playground version of the current master branch on GitHub Pages: [https://wordpress.github.io/gutenberg/](https://wordpress.github.io/gutenberg/)
diff --git a/gb-src/docs/contributors/git-workflow.md b/gb-src/docs/contributors/git-workflow.md
new file mode 100644
index 0000000000000..7f1499bc7de34
--- /dev/null
+++ b/gb-src/docs/contributors/git-workflow.md
@@ -0,0 +1,65 @@
+# Git Workflow
+
+A good workflow for new contributors to follow is listed below:
+- Fork Gutenberg repository
+- Clone forked repository
+- Create a new branch
+- Make code changes
+- Commit code changes within the newly created branch
+- Push branch to forked repository
+- Submit Pull Request to Gutenberg repository
+
+Ideally name your branches with prefixes and descriptions, like this: `[type]/[change]`. A good prefix would be:
+
+- `add/` = add a new feature
+- `try/` = experimental feature, "tentatively add"
+- `update/` = update an existing feature
+
+For example, `add/gallery-block` means you're working on adding a new gallery block.
+
+You can pick among all the tickets, or some of the ones labelled Good First Issue.
+
+The workflow is documented in greater detail in the [repository management](/docs/contributors/repository-management.md) document.
+
+## Keeping Your Branch Up To Date
+
+When many different people are working on a project simultaneously, pull requests can go stale quickly. A "stale" pull request is one that is no longer up to date with the main line of development, and it needs to be updated before it can be merged into the project.
+
+There are two ways to do this: merging and rebasing. In Gutenberg, the recommendation is to rebase. Rebasing means rewriting your changes as if they're happening on top of the main line of development. This ensures the commit history is always clean and linear. Rebasing can be performed as many times as needed while you're working on a pull request. **Do share your work early on** by opening a pull request and keeping your history rebase as you progress.
+
+The main line of development is known as the `master` branch. If you have a pull-request branch that cannot be merged into `master` due to a conflict (this can happen for long-running pull requests), then in the course of rebasing you'll have to manually resolve any conflicts in your local copy. Learn more in [section _Perform a rebase_](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request#perform-a-rebase) of _How to Rebase a Pull Request_.
+
+Once you have resolved any conflicts locally you can update the pull request with `git push --force-with-lease`. Using the `--force-with-lease` parameter is important to guarantee that you don't accidentally overwrite someone else's work.
+
+To sum it up, you need to fetch any new changes in the repository, rebase your branch on top of `master`, and push the result back to the repository. These are the corresponding commands:
+
+```sh
+git fetch
+git rebase master
+git push --force-with-lease your-branch-name
+```
+
+## Keeping Your Fork Up To Date
+
+Working on pull request starts with forking the Gutenberg repository, your separate working copy. Which can easily go out of sync as new pull requests are merged into the main repository. Here your working repository is a `fork` and the main Gutenberg repository is `upstream`. When working on new pull request you should always update your fork before you do `git checkout -b my-new-branch` to work on a feature or fix.
+
+To sync your fork you need to fetch the upstream changes and merge them into your fork. These are the corresponding commands:
+
+``` sh
+git fetch upstream
+git checkout master
+git merge upstream/master
+```
+
+This will update you local copy to update your fork on github push your changes
+
+```
+git push
+```
+
+The above commands will update your `master` branch from _upstream_. To update any other branch replace `master` with the respective branch name.
+
+
+## References
+- https://git-scm.com/book/en/v2
+- https://help.github.com/categories/collaborating-with-issues-and-pull-requests/
diff --git a/gb-src/docs/contributors/grammar.md b/gb-src/docs/contributors/grammar.md
new file mode 100644
index 0000000000000..7d7e9bf73b8c0
--- /dev/null
+++ b/gb-src/docs/contributors/grammar.md
@@ -0,0 +1,6 @@
+
+# Block Grammar
+
+
diff --git a/gb-src/docs/contributors/history.md b/gb-src/docs/contributors/history.md
new file mode 100644
index 0000000000000..a0f05a9cb446a
--- /dev/null
+++ b/gb-src/docs/contributors/history.md
@@ -0,0 +1,19 @@
+# History
+
+## Survey
+There was a survey done: [https://make.wordpress.org/core/2017/04/07/editor-experience-survey-results/](https://make.wordpress.org/core/2017/04/07/editor-experience-survey-results/)
+
+## Inspiration
+This includes a list of historical articles and influences on the Gutenberg project.
+
+- LivingDocs: [https://beta.livingdocs.io/articles](https://beta.livingdocs.io/articles)
+- Parrot: [https://intenseminimalism.com/2017/parrot-an-integrated-site-builder-and-editor-concept-for-wordpress/](https://intenseminimalism.com/2017/parrot-an-integrated-site-builder-and-editor-concept-for-wordpress/)
+- Apple Keynote
+- Slack
+- Google Sites v2
+
+## Blog posts by the team
+
+- Gutenberg tag on make/core: updates and much more: [https://make.wordpress.org/core/tag/gutenberg/](https://make.wordpress.org/core/tag/gutenberg/)
+- Suggested revised timeline: [https://make.wordpress.org/core/2017/08/11/revised-suggested-roadmap-for-gutenberg-and-customization/](https://make.wordpress.org/core/2017/08/11/revised-suggested-roadmap-for-gutenberg-and-customization/)
+- Discovering Gutenberg: [https://make.wordpress.org/core/2017/08/08/discovering-gutenberg-and-next-steps/](https://make.wordpress.org/core/2017/08/08/discovering-gutenberg-and-next-steps/)
diff --git a/gb-src/docs/contributors/localizing.md b/gb-src/docs/contributors/localizing.md
new file mode 100644
index 0000000000000..66d8df9568296
--- /dev/null
+++ b/gb-src/docs/contributors/localizing.md
@@ -0,0 +1,9 @@
+# Localizing Gutenberg Plugin
+
+To translate Gutenberg in your locale or language, [select your locale here](https://translate.wordpress.org/projects/wp-plugins/gutenberg) and translate *Development* (which contains the plugin's string) and/or *Development Readme* (please translate what you see in the Details tab of the [plugin page](https://wordpress.org/plugins/gutenberg/)).
+
+A Global Translation Editor (GTE) or Project Translation Editor (PTE) with suitable rights will process your translations in due time.
+
+Language packs are automatically generated once 95% of the plugin's strings are translated and approved for a locale.
+
+The inclusion of Gutenberg into WordPress core means that more than 51% of WordPress installations running a translated WordPress installation have Gutenberg's translated strings compiled into the core language pack as well.
diff --git a/gb-src/docs/contributors/managing-packages.md b/gb-src/docs/contributors/managing-packages.md
new file mode 100644
index 0000000000000..000a74026469f
--- /dev/null
+++ b/gb-src/docs/contributors/managing-packages.md
@@ -0,0 +1,8 @@
+# Managing Packages
+
+This repository uses [lerna] to manage Gutenberg modules and publish them as packages to [npm]. This enforces certain steps in the workflow which are described in details in [packages](/packages/README.md) documentation.
+
+Maintaining dozens of npm packages is difficult—it can be tough to keep track of changes. That's why we use `CHANGELOG.md` files for each package to simplify the release process. As a contributor you should add an entry to the aforementioned file each time you contribute adding production code as described in [Maintaining Changelogs](/packages/README.md#maintaining-changelogs) section.
+
+[lerna]: https://lernajs.io/
+[npm]: https://www.npmjs.com/
diff --git a/gb-src/docs/contributors/outreach.md b/gb-src/docs/contributors/outreach.md
new file mode 100644
index 0000000000000..208a1eaf707e1
--- /dev/null
+++ b/gb-src/docs/contributors/outreach.md
@@ -0,0 +1,65 @@
+# Outreach
+
+This includes articles, talks, demos and anything the community is doing to discuss, learn about, and contribute to Gutenberg. This is not an exhaustive list; if we are missing your event or article, just let us know.
+
+## Articles
+
+A short list of useful articles around defining, extending, and contributing to Gutenberg.
+
+### Overviews of Gutenberg
+
+- [Gutenberg, or the Ship of Theseus](https://matiasventura.com/post/gutenberg-or-the-ship-of-theseus/), Matías Ventura Bausero (October 2017)
+- [We Called It Gutenberg for a Reason](https://ma.tt/2017/08/we-called-it-gutenberg-for-a-reason/), Matt Mullenweg (August 2017)
+- [How Gutenberg is Changing WordPress Development](https://riad.blog/2017/10/06/how-gutenberg-is-changing-wordpress-development/), Riad Benguella (October 2017)
+- [How Gutenberg Will Shape the Future of WordPress](https://www.linkedin.com/pulse/gutenberg-morten-rand-hendriksen/), Morten Rand-Henrikson (August 2017)
+
+### Extending Gutenberg
+
+- [With Gutenberg, what happens to my Custom Fields?](https://riad.blog/2017/12/11/with-gutenberg-what-happens-to-my-custom-fields/), Riad Benguella (December 2017)
+- [One thousand and one ways to extend Gutenberg today](https://riad.blog/2017/10/16/one-thousand-and-one-way-to-extend-gutenberg-today/), Riad Benguella (October 2017)
+- [Gutenberg Plugin Boilerplate](https://github.com/ahmadawais/Gutenberg-Boilerplate/), Ahmad Awais (August 2017)
+
+### Community Contribution
+
+- [Gutenberg Block Library](https://editorblockswp.com/library), Danny Cooper (August 2018)
+- [A zero-configuration developer toolkit for building WordPress Gutenberg block plugins](https://ahmadawais.com/create-guten-block-toolkit/), Ahmad Awais (January 2018)
+- [Contributing to Gutenberg Without Code](https://wordimpress.com/a-pot-stirrer-amongst-chefs-contributing-to-gutenberg-without-code/), Kevin Hoffman (August 2017)
+- [Testing Flow in Gutenberg: Instructions for how to contribute to usability testing](https://make.wordpress.org/test/2017/11/22/testing-flow-in-gutenberg/), Anna Harrison (November 2017)
+
+### Article Compilations
+
+- [Curated Collection of Gutenberg Articles, Plugins, Blocks, Tutorials, etc](http://gutenberghub.com/), By Munir Kamal
+- [Articles about Gutenberg](https://github.com/WordPress/gutenberg/issues/1419) (Github Issue thread with links)
+- [Gutenberg articles on ManageWP.org](https://managewp.org/search?q=gutenberg)
+- [Gutenberg Times](https://gutenbergtimes.com/category/updates/)
+
+## Talks
+
+Talks given about Gutenberg, including slides and videos as they are available.
+
+### Slides
+- [The new core WordPress editor](http://kimb.me/talk-bigwp-london-new-core-wordpress-editor/) at BigWP London (18. May 2017)
+- [Gutenberg Notes](http://haiku2.com/2017/09/bend-wordpress-meetup-gutenberg-notes/) at Bend WordPress Meetup (5. September 2017)
+- [Gutenberg and the Future of Content in WordPress](https://www.slideshare.net/andrewmduthie/gutenberg-and-the-future-of-content-in-wordpress) (20. September 2017)
+- [Head first into Gutenberg](https://speakerdeck.com/prtksxna/head-first-into-gutenberg) at the [WordPress Goa Meet-up](https://www.meetup.com/WordPressGoa/events/245275573/) (1. December 2017)
+- [Gutenberg : vers une approche plus fine du contenu](https://imathi.eu/2018/02/16/gutenberg-vers-une-approche-plus-fine-du-contenu/) at [WP Paris](https://wpparis.fr/) (8. February 2018)
+
+### Videos
+- [All `Gutenberg` tagged Talks at WordPress.tv](https://wordpress.tv/tag/gutenberg/)
+- 2018-Jun - [Beyond Gutenberg](https://wordpress.tv/2018/07/09/matias-ventura-beyond-gutenberg/) by Matías Ventura
+- 2018-Jun - [Anatomy of a block: Gutenberg design patterns](https://wordpress.tv/2018/07/08/tammie-lister-anatomy-of-a-block-gutenberg-design-patterns/) by Tammie Lister
+- 2017-Dec - [State of the Word 2017](https://wordpress.tv/2017/12/04/matt-mullenweg-state-of-the-word-2017/) by Matt Mullenweg (Gutenberg demo by Matías Ventura at 35:00)
+- [Gutenberg is Coming (Don’t Be Afraid)](https://training.ithemes.com/webinar/gutenberg-is-coming-dont-be-afraid/) from iThemes Training
+
+## Showcases or demonstrations:
+
+https://wpleeds.co.uk/events/plugins-gutenberg-wordpress-leeds-july-2017/
+
+http://kimb.me/talk-bigwp-london-new-core-wordpress-editor
+
+https://www.facebook.com/events/278785795934302/
+
+https://www.meetup.com/WordPress-Melbourne/events/241543639
+
+https://wpmeetups.de/termin/29-wp-meetup-stuttgart-gutenberg-editor-rueckblick-wordcamp-europe/
+
diff --git a/gb-src/docs/contributors/principles.md b/gb-src/docs/contributors/principles.md
new file mode 100644
index 0000000000000..8acedac095826
--- /dev/null
+++ b/gb-src/docs/contributors/principles.md
@@ -0,0 +1,17 @@
+# Principles
+
+First, let’s look at the big picture. If the architectural and UX principles described here are activated at scale, how will the Gutenberg project improve and transform both users and creators experiences?
+
+How Gutenberg can transform the *user experience*:
+
+* Users can focus on conveying their ideas and information in the way they want without having to understand the underlying technical / semantic distinctions.
+* Users can add and edit functionality more easily via the unified mechanism of blocks.
+* Everything on a user’s site can be directly manipulated and edited in place without having to rely on traversing complex navigation menus and disparate sections.
+* Users only have to learn a single interface — the block — and a single way to add new elements. They will gain the confidence that comes from using a system that feels unified and clear, where everything works in a consistent manner.
+
+How Gutenberg can transform the *developer and designer experience*:
+
+* A powerful and expressive toolkit that allows crafting first-class experiences through standardized design and development processes.
+* This standardization allows for interoperability — developers can create components that seamlessly connect with components from other developers.
+* Relying on consistent UX patterns means developers can be confident their work will be immediately familiar and usable to users and that they don’t have to reinvent interaction patterns. They can focus on their product.
+* With one modern, flexible interface, the block, but many ways to bend it, makers have an opportunity to extend WordPress in many new ways.
diff --git a/gb-src/docs/contributors/principles/the-block.md b/gb-src/docs/contributors/principles/the-block.md
new file mode 100644
index 0000000000000..bdb0c920c77c1
--- /dev/null
+++ b/gb-src/docs/contributors/principles/the-block.md
@@ -0,0 +1,27 @@
+# Blocks are the Interface
+
+At the core of Gutenberg lies the concept of the block. From a technical point of view, blocks both raise the level of abstraction from a single document to a collection of meaningful elements, and they replace ambiguity—inherent in HTML—with explicit structure.
+
+From a user perspective, blocks allow any kind of content, media, or functionality to be directly added to their site in a more consistent and usable way. The “add block” button gives the user access to an entire library of options all in one place, rather than having to hunt through menus or know shortcodes.
+
+But most importantly, Gutenberg is built on the principle of *direct manipulation*, which means that the primary options for how an element is displayed are controlled *in the context of the block itself*. This is a big shift from the traditional WordPress model, where options that were often buried deep in layers of navigation menus controlled the elements on a page through indirect mechanisms.
+
+So, for example, a user can add an image, write its caption, change its width and layout, add a link around it, all from within the block interface in the canvas. The same principle should apply to more complex blocks, like a "navigation menu", with the user being able to add, edit, move, and finalize the full presentation of their navigation.
+
+* Users only need to learn one interface — the block — to add and edit everything on their site. Users shouldn’t have to write shortcodes, custom HTML, or understand hidden mechanisms to embed content.
+* Gutenberg makes core features more discoverable, reducing hard-to-find “Mystery meat.” WordPress supports a large number of blocks and 30+ embeds. Let’s increase their visibility.
+
+## Building Blocks
+
+What does this mean for designers and developers? The block structure plus the principle of direct manipulation mean thinking differently about how to design and develop WordPress components. Let’s take another look at the architecture of a block:
+
+
+
+### The primary interface for a block is the content area of the block.
+The placeholder content in the content area of the block can be thought of as a guide or interface for users to follow a set of instructions or “fill in the blanks” (more on placeholders later). Since the content area represents what will actually appear on the site, interaction here hews closest to the principle of direct manipulation and will be most intuitive to the user. This should be thought of as the primary interface for adding and manipulating content and adjusting how it is displayed.
+
+### The block toolbar is the place for critical options that can’t be incorporated into placeholder UI.
+Basic block settings won’t always make sense in the context of the placeholder / content UI. As a secondary option, options that are critical to the functionality of a block can live in the block toolbar. The block toolbar is one step removed from direct manipulation, but is still highly contextual and visible on all screen sizes, so it is a great secondary option.
+
+### The Settings Sidebar should only be used for advanced, tertiary controls.
+The Settings Sidebar is not visible by default on a small / mobile screen, and may also be collapsed even in a desktop view. Therefore, it should not be relied on for anything that is necessary for the basic operation of the block. Pick good defaults, make important actions available in the block toolbar, and think of the sidebar as something that only power users may discover.
diff --git a/gb-src/docs/contributors/readme.md b/gb-src/docs/contributors/readme.md
new file mode 100644
index 0000000000000..96c3a85424e5d
--- /dev/null
+++ b/gb-src/docs/contributors/readme.md
@@ -0,0 +1,18 @@
+# Contributor Documentation
+
+Welcome to the Gutenberg Project Contributors Guide.
+
+The following guidelines are in place to create consistency across the project and the numerous contributors. See the [Contributing Documentation](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md) for technical details around setup, and submitting your contributions.
+
+## Philosophy
+
+* [Architectural and UX Principles of Gutenberg](/docs/contributors/principles.md)
+
+## Sections
+
+The contributors guide has the following different sections by contribution type:
+
+* [Design Contributions](/docs/contributors/design.md)
+* [Developer Contributions](/docs/contributors/develop.md)
+* [Documentation Contributions](/docs/contributors/document.md)
+
diff --git a/gb-src/docs/contributors/reference.md b/gb-src/docs/contributors/reference.md
new file mode 100644
index 0000000000000..95c0b603323e5
--- /dev/null
+++ b/gb-src/docs/contributors/reference.md
@@ -0,0 +1,17 @@
+# Reference
+
+- [Glossary](/docs/designers-developers/glossary.md)
+- [Coding Guidelines](/docs/contributors/coding-guidelines.md)
+- [Testing Overview](/docs/contributors/testing-overview.md)
+- [Frequently Asked Questions](/docs/designers-developers/faq.md)
+
+## Logo
+
+
+Released under GPL license, made by [Cristel Rossignol](https://twitter.com/cristelrossi).
+
+[Download the SVG logo](https://github.com/WordPress/gutenberg/blob/master/docs/final-g-wapuu-black.svg).
+
+## Mockups
+
+Mockup Sketch files are available in [the Design section](/docs/designers-developers/designers/design-resources.md).
diff --git a/gb-src/docs/contributors/release-screenshot.png b/gb-src/docs/contributors/release-screenshot.png
new file mode 100644
index 0000000000000..63334f489c0b2
Binary files /dev/null and b/gb-src/docs/contributors/release-screenshot.png differ
diff --git a/gb-src/docs/contributors/release.md b/gb-src/docs/contributors/release.md
new file mode 100644
index 0000000000000..8b3fb3f41b745
--- /dev/null
+++ b/gb-src/docs/contributors/release.md
@@ -0,0 +1,260 @@
+# Gutenberg Release Process
+
+This Repository is used to perform several types of releases. This document serves as a checklist for each one of these. It is helpful if you'd like to understand the different workflows.
+
+To release Gutenberg, you need commit access to the [WordPress.org plugin repository][plugin repository]. 🙂
+
+## Plugin Releases
+
+### Schedule
+
+We release a new major version approximately every two weeks. The current and next versions are [tracked in GitHub milestones](https://github.com/WordPress/gutenberg/milestones), along with each version's tagging date.
+
+On the date of the current milestone, we publish a release candidate and make it available for plugin authors and users to test. If any regressions are found with a release candidate, a new release candidate can be published.
+
+The date in the milestone is the date of **tagging the release candidate**. On this date, all remaining PRs on the milestone are moved automatically to the next release.
+
+Release candidates should be versioned incrementally, starting with `-rc.1`, then `-rc.2`, and so on.
+
+Two days after the first release candidate, the stable version is created based on the last release candidate and any necessary regression fixes.
+
+Once the stable version is released, a post [like this](https://make.wordpress.org/core/2019/06/26/whats-new-in-gutenberg-26th-june/) describing the changes and performing a performance audit should be published.
+
+If critical bugs are discovered on stable versions of the plugin, patch versions can be released at any time.
+
+### Release Tool
+
+The plugin release process is entirely automated. To release the RC version of the plugin, run the following command and follow the instructions: (Note that at the time of writing, the tool doesn't support releasing multiple consecutive RC releases)
+
+```bash
+./bin/commander.js rc
+```
+
+To release a stable version, run:
+
+```bash
+./bin/commander.js stable
+```
+
+It is possible to run the "stable" release CLI in a consecutive way to release patch releases following the first stable release.
+
+### Manual Release Process
+
+#### Creating the first Release Candidate
+
+Releasing the first release candidate for this milestone (`x.x`) involves:
+
+1. writing a release blog post and changelog
+2. creating the release branch
+3. bumping the version and tagging the release
+4. building the plugin
+5. publishing the release to GitHub
+6. publishing the call for testing
+
+##### Writing the Release Post and Changelog
+
+1. Open the [list of closed pull requests](https://github.com/WordPress/gutenberg/pulls?utf8=✓&q=is%3Apr+is%3Aclosed+sort%3Acreated-desc+) and filter by the current milestone.
+2. Read through each PR to determine if it needs to be included in the blog post and/or changelog.
+3. Choose a few features to highlight in the release post; record an animation of them in use.
+4. Save the draft post on [make.wordpress.org/core](https://make.wordpress.org/core/); this post should be published after the actual release.
+
+##### Creating the Release Branch
+
+For each milestone (let's assume it's `x.x` here), a release branch is used to release all RCs and minor releases. For the first RC of the milestone, a release branch is created from master.
+
+```
+git checkout master
+git checkout -b release/x.x
+git push origin release/x.x
+```
+
+##### Bumping the Version and Tagging the Release
+
+1. Checkout the `release/x.x` branch.
+2. Create [a commit like this](https://github.com/WordPress/gutenberg/pull/13125/commits/13fa651dadc2472abb9b95f80db9d5f23e63ae9c), bumping the version number in `gutenberg.php`, `package.json`, and `package-lock.json` to `x.x.0-rc.1`.
+3. Create a Pull Request from the release branch into `master` using the changelog as a description and ensure the tests pass properly.
+4. Tag the RC version. `git tag vx.x.0-rc.1` from the release branch.
+5. Push the tag `git push --tags`.
+6. Merge the version bump pull request and avoid removing the release branch.
+
+##### Build the Plugin
+
+1. Run `git fetch --tags`.
+2. Check out the tag for this release, you should run `git checkout vx.x.0-rc.1`.
+3. Run `./bin/build-plugin-zip.sh` from the root of project. This packages a zip file with a release build of `gutenberg.zip`.
+
+##### Publish the Release on GitHub
+
+1. [Create a new release on GitHub](https://github.com/WordPress/gutenberg/releases/new).
+2. If you were releasing the `x.x.0-rc.1` release candidate, label it `x.x.0-rc.1` and use the `vx.x.x-rc.1` as a tag.
+3. Upload the a `gutenberg.zip` file into the release.
+4. Use the changelog as a description of the release.
+5. Publish the release.
+
+Here's an example [release candidate page](https://github.com/WordPress/gutenberg/releases/tag/v4.6.0-rc.1); yours should look like that when you're finished.
+
+#### Creating Release Candidate Patches (done via `git cherry-pick`)
+
+If a bug is found in a release candidate and a fix is committed to `master`, we should include that fix in a new release candidate. To do this you'll need to use `git cherry-pick` to add these changes to the milestone's release branch. This way only fixes are added to the release candidate and not all the new code that has landed on `master` since tagging:
+
+1. Checkout the corresponding release branch with: `git checkout release/x.x`.
+2. Cherry-pick fix commits (in chronological order) with `git cherry-pick [SHA]`.
+3. Create [a commit like this](https://github.com/WordPress/gutenberg/pull/13125/commits/13fa651dadc2472abb9b95f80db9d5f23e63ae9c), bumping the version number in `gutenberg.php`, `package.json`, and `package-lock.json` to `x.x.0-rc.2`.
+4. Create a Pull Request from the release branch into `master` using the changelog as a description and ensure the tests pass properly.
+5. Tag the RC version. `git tag vx.x.0-rc.2` from the release branch.
+6. Push the tag `git push --tags`.
+7. Merge the version bump pull request and avoid removing the release branch.
+8. Follow the steps in [build the plugin](#build-the-plugin) and [publish the release on GitHub](#publish-the-release-on-github).
+
+You can copy the existing changelog from the previous release candidate. Let other contributors know that a new release candidate has been released in the [`#core-editor` channel](https://wordpress.slack.com/messages/C02QB2JS7) and the call for testing post.
+
+### Official Gutenberg Releases™
+
+The process of releasing Gutenberg is similar to creating a release candidate, except we don't use the `-rc.X` in the `git` tag and we publish a new branch in the subversion repository. This updates the version available in the WordPress plugin repository and will cause WordPress sites around the world to prompt users to update to this new version.
+
+#### Creating a Release
+
+Creating a release involves:
+
+1. verifying the release blog post and changelog
+2. bumping the version
+3. building the plugin
+4. publishing the new release to GitHub
+5. committing to the [plugin repository]
+6. publishing the release blog post
+
+##### Verifying the Release Post and Changelog
+
+1. Check the draft post on [make.wordpress.org/core](https://make.wordpress.org/core/); make sure the changelog reflects what's shipping in the release.
+
+##### Bumping the Version
+
+1. Checkout the release branch `git checkout release/x.x`.
+
+**Note:** This branch should never be removed or rebased. When we want to merge something from it to master and conflicts exist/may exist we use a temporary branch `bump/x.x`.
+
+2. Create [a commit like this](https://github.com/WordPress/gutenberg/commit/00d01049685f11f9bb721ad3437cb928814ab2a2#diff-b9cfc7f2cdf78a7f4b91a753d10865a2), removing the `-rc.X` from the version number in `gutenberg.php`, `package.json`, and `package-lock.json`.
+3. Create a new branch called `bump/x.x` from `release/x.x` and switch to it: `git checkout -b bump/x.x`.
+4. Create a pull request from `bump/x.x` to `master`. Verify the continuous integrations tests pass, before continuing to the next step even if conflicts exist.
+5. Rebase `bump/x.x` against `origin/master` using `git fetch origin && git rebase origin/master`.
+6. Force push the branch `bump/x.x` using `git push --force-with-lease`.
+7. Switch to the `release/x.x` branch. Tag the version from the release branch `git tag vx.x.0`.
+8. Push the tag `git push --tags`.
+9. Merge the version bump pull request.
+
+
+##### Build the Plugin
+
+1. Run `git fetch --tags`.
+2. Check out the tag for this release, you should run `git checkout vx.x.0`.
+3. Run `./bin/build-plugin-zip.sh` from the root of project. This packages a zip file with a release build of `gutenberg.zip`.
+
+##### Publish the Release on GitHub
+
+1. [Create a new release on GitHub](https://github.com/WordPress/gutenberg/releases/new).
+2. If you were releasing the `x.x.0` release candidate, label it `x.x.0` and use the `vx.x.x` as a tag.
+3. Upload the a `gutenberg.zip` file into the release.
+4. Use the changelog as a description of the release.
+5. Publish the release.
+
+##### Commit to the Plugin Repository
+
+You'll need to use Subversion to publish the plugin to WordPress.org.
+
+1. Do an SVN checkout of `https://wordpress.org/plugins/gutenberg/trunk`:
+ * If this is your first checkout, run: `svn checkout https://plugins.svn.wordpress.org/gutenberg/trunk`
+ * If you already have a copy, run: `svn up`
+2. Delete the contents except for the `readme.txt` and `changelog.txt` files (these files don’t exist in the `git` repo, only in Subversion).
+3. Extract the contents of the zip file.
+4. Edit `readme.txt`, replacing the changelog for the previous version with the current release's changelog.
+5. Add the changelog for the current release to `changelog.txt`.
+6. Add new files/remove deleted files from the repository:
+```bash
+# Add new files:
+svn st | grep '^\?' | awk '{print $2}' | xargs svn add
+# Delete old files:
+svn st | grep '^!' | awk '{print $2}' | xargs svn rm
+```
+7. Commit the new version:
+```bash
+# Replace X.X.X with your version:
+svn ci -m "Committing Gutenberg version X.X.X"
+```
+8. Tag the new version:
+```bash
+svn cp https://plugins.svn.wordpress.org/gutenberg/trunk https://plugins.svn.wordpress.org/gutenberg/tags/X.X.X -m "Tagging Gutenberg version X.X.X"
+```
+9. Edit `readme.txt` to point to the new tag. The **Stable version** header in `readme.txt` should be updated to match the new release version number. After updating and committing that, the new version should be released:
+```bash
+svn ci -m "Releasing Gutenberg version X.X.X"
+```
+
+This will cause the new version to be available to users of WordPress all over the globe! 💃
+
+You should check that folks are able to install the new version from their Dashboard.
+
+### Publish the Release Blog Post
+
+1. Publish the [make/core](https://make.wordpress.org/core/) release blog post drafted earlier.
+2. Pat yourself on the back! 👍
+
+If you don't have access to [make.wordpress.org/core](https://make.wordpress.org/core/), ping [someone on the Gutenberg Core team](https://github.com/orgs/WordPress/teams/gutenberg-core) in the [WordPress #core-editor Slack channel](https://wordpress.slack.com/messages/C02QB2JS7) to publish the post.
+
+## Packages Releases and WordPress Core Updates
+
+The Gutenberg repository mirrors the [WordPress SVN repository](https://make.wordpress.org/core/handbook/about/release-cycle/) in terms of branching for each SVN branch, a corresponding Gutenberg `wp/*` branch is created:
+
+ - The `wp/trunk` branch contains all the packages that are published and used in the `trunk` branch of WordPress.
+ - A Gutenberg branch targeting a specific WordPress major release (including its further minor increments) is created (example `wp/5.2`) based on the `wp/trunk` Gutenberg branch when the WordPress `trunk` branch is marked as "feature-freezed". (This usually happens when the first `beta` of the next WordPress major version is released).
+
+### Synchronizing WordPress Trunk
+
+For each Gutenberg plugin release, WordPress trunk should be synchronized with this release. This involves the following steps:
+
+**Note:** The WordPress `trunk` branch can be closed or in "feature-freeze" mode. Usually, this happens between the first `beta` and the first `RC` of the WordPress release cycle. During this period, the Gutenberg plugin releases should not be synchronized with WordPress Core.
+
+1. Ensure the WordPress `trunk` branch is open for enhancements.
+2. Check out the last published Gutenberg release branch `git checkout release/x.x`
+3. Create a Pull Request from this branch targeting `wp/trunk`.
+4. Merge the Pull Request using the "Rebase and Merge" button to keep the history of the commits.
+
+Now, the branch is ready to be used to publish the npm packages.
+
+1. Check out the `wp/trunk` branch.
+2. Run the [package release process] but when asked for the version numbers to choose for each package, (assuming the package versions are written using this format `major.minor.patch`) make sure to bump at least the `minor` version number. For example, if the CHANGELOG of the package to be released indicates that the next unreleased version is `5.6.1`, choose `5.7.0` as a version.
+3. Update the `CHANGELOG.md` files of the published packages with the new released versions and commit to the `wp/trunk` branch.
+4. Cherry-pick the "Publish" (created by Lerna) and the CHANGELOG update commits into the `master` branch of Gutenberg.
+
+Now, the npm packages should be ready and a patch can be created and committed into WordPress `trunk`.
+
+
+### Minor WordPress Releases
+
+The following workflow is needed when bug fixes or security releases need to be backported into WordPress Core. This can happen in a few use-cases:
+
+ - During the `beta` and the `RC` period of the WordPress release cycle.
+ - For WordPress minor releases and WordPress security releases (example `5.1.1`).
+
+1. Cherry-pick
+2. Check out the last published Gutenberg release branch `git checkout release/x.x`
+3. Create a Pull Request from this branch targeting the WordPress related major branch (Example `wp/5.2`).
+4. Merge the Pull Request using the "Rebase and Merge" button to keep the history of the commits.
+
+Now, the branch is ready to be used to publish the npm packages.
+
+1. Check out the WordPress branch used before (Example `wp/5.2`).
+2. Run the [package release process] but when asked for the version numbers to choose for each package, (assuming the package versions are written using this format `major.minor.patch`) make sure to bump only the `patch` version number. For example, if the last published package version for this WordPress branch was `5.6.0`, choose `5.6.1` as a version.
+
+**Note:** For WordPress `5.0` and WordPress `5.1`, a different release process was used. This means that when choosing npm package versions targeting these two releases, you won't be able to use the next `patch` version number as it may have been already used. You should use the "metadata" modifier for these. For example, if the last published package version for this WordPress branch was `5.6.1`, choose `5.6.1+patch.1` as a version.
+
+3. Update the `CHANGELOG.md` files of the published packages with the new released versions and commit to the corresponding branch (Example `wp/5.2`).
+4. Cherry-pick the CHANGELOG update commits into the `master` branch of Gutenberg.
+
+Now, the npm packages should be ready and a patch can be created and committed into the corresponding WordPress SVN branch.
+
+---------
+
+Ta-da! 🎉
+
+[plugin repository]: https://plugins.trac.wordpress.org/browser/gutenberg/
+[package release process]: https://github.com/WordPress/gutenberg/blob/master/packages/README.md#releasing-packages
diff --git a/gb-src/docs/contributors/repository-management.md b/gb-src/docs/contributors/repository-management.md
new file mode 100644
index 0000000000000..cf7dbcee1b25e
--- /dev/null
+++ b/gb-src/docs/contributors/repository-management.md
@@ -0,0 +1,186 @@
+# Repository Management
+
+This is a living document explaining how we collaboratively manage the Gutenberg repository. If you’d like to suggest a change, please open an issue for discussion or submit a pull request to the document.
+
+This document covers:
+
+- [Issues](#issues)
+ - [Labels](#labels)
+ - [Milestones](#milestones)
+ - [Triaging Issues](#triaging-issues)
+- [Pull Requests](#pull-requests)
+ - [Code Review](#code-review)
+ - [Design Review](#design-review)
+ - [Merging Pull Requests](#merging-pull-requests)
+ - [Closing Pull Requests](#closing-pull-requests)
+- [Projects](#projects)
+
+## Issues
+
+A healthy issue list is one where issues are relevant and actionable. *Relevant* in the sense that they relate to the project’s current priorities. *Actionable* in the sense that it’s clear what action(s) need to be taken to resolve the issue.
+
+Any issues that are irrelevant or not actionable should be closed, because they get in the way of making progress on the project. Imagine the issue list as a desk: the more clutter you have on it, the more difficult it is to use the space to get work done.
+
+### Labels
+
+All issues should have [one or more labels](https://github.com/WordPress/gutenberg/labels).
+
+Workflow labels start with “Needs” and may be applied as needed. Ideally, each workflow label will have a group that follows it, such as the Accessibility Team for `Needs Accessibility Feedback`, the Testing Team for `Needs Testing`, etc.
+
+[Priority High](https://github.com/WordPress/gutenberg/labels/Priority%20High) and [Priority OMGWTFBBQ](https://github.com/WordPress/gutenberg/labels/Priority%20OMGWTFBBQ) issues should have an assignee and/or be in an active milestone.
+
+Help requests or 'how to' questions should be posted in a relevant support forum as a first step. If something might be a bug but it's not clear, the Support Team or a forum volunteer can help troubleshoot the case to help get all the right information needed for an effective bug report.
+
+Here are some labels you might commonly see:
+
+- [Good First Issue](https://github.com/WordPress/gutenberg/labels/Good%20First%20Issue) - Issues identified as good for new contributors to work on. Comment to note that you intend to work on the issue and reference the issue number in the pull request you submit.
+- [Good First Review](https://github.com/WordPress/gutenberg/labels/Good%20First%20Review) - Pull requests identified as good for new contributors who are interested in doing code reviews.
+- [Needs Accessibility Feedback](https://github.com/WordPress/gutenberg/labels/Accessibility) - Changes that impact accessibility and need corresponding review (e.g. markup changes).
+- [Needs Design Feedback](https://github.com/WordPress/gutenberg/labels/Needs%20Design%20Feedback) - Changes that modify the design or user experience in some way and need sign-off.
+- [[Type] Bug](https://github.com/WordPress/gutenberg/labels/%5BType%5D%20Bug) - An existing feature is broken in some way.
+- [[Type] Enhancement](https://github.com/WordPress/gutenberg/labels/%5BType%5D%20Enhancement) - Gutenberg would be better with this improvement added.
+- [[Type] Plugin / Extension Conflict](https://github.com/WordPress/gutenberg/labels/%5BType%5D%20Plugin%20%2F%20Extension%20Conflict) - Documentation of a conflict between Gutenberg and a plugin or extension. The plugin author should be informed and provided documentation on how to address.
+- [[Status] Needs More Info](https://github.com/WordPress/gutenberg/labels/%5BStatus%5D%20Needs%20More%20Info) - The issue needs more information in order to be actionable and relevant. Typically this requires follow-up from the original reporter.
+
+[Check out the label directory](https://github.com/WordPress/gutenberg/labels) for a listing of all labels.
+
+### Milestones
+
+We put issues into [milestones](https://github.com/wordpress/gutenberg/milestones) to better categorize them. Issues are added to milestones starting with `WordPress` and pull requests are added to milestones ending in `(Gutenberg)`.
+
+Here are some milestones you might see:
+
+- [WordPress X.Y](https://github.com/WordPress/gutenberg/milestone/70): Tasks that should be done for future WordPress releases.
+- [X.Y (Gutenberg)](https://github.com/WordPress/gutenberg/milestone/85): PRs targeted for the Gutenberg Plugin X.Y release.
+- [Future](https://github.com/WordPress/gutenberg/milestone/35): this is something that is confirmed by everyone as a good thing but doesn’t fall into other criteria.
+
+### Triaging Issues
+
+To keep the issue list healthy, it needs to be triaged regularly. *Triage* is the practice of reviewing existing issues to make sure they’re relevant, actionable, and have all the information they need.
+
+Anyone can help triage, although you’ll need contributor permission on the Gutenberg repository to modify an issue’s labels or edit its title.
+
+To start simply choose from one of these filtered lists of issues:
+
+- [All Gutenberg issues without an assigned label](https://github.com/wordpress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc+no%3Alabel). Triaging by simply adding labels helps people focused on certain aspects of Gutenberg find relevant issues easier and start working on them.
+- [The least recently updated Gutenberg issues](https://github.com/WordPress/gutenberg/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc). Triaging issues that are getting old and possibly out of date keeps important work from being overlooked.
+- [All Gutenberg issues with no comments](https://github.com/WordPress/gutenberg/issues?q=is%3Aopen+is%3Aissue+sort%3Acomments-asc) Triaging this list helps make sure all issues are acknowledged, and can help identify issues that may need more information or discussion before they are actionable.
+- [The least commented on issues](https://github.com/WordPress/gutenberg/issues?q=is%3Aopen+is%3Aissue+sort%3Acomments-asc) Triaging this list helps the community figure out things like traction for certain proposals.
+
+You can also create your own custom set of filters on GitHub. If you have a filter you think might be useful for the community, feel free to submit a PR to add it to this list.
+
+When triaging, either one of the lists above or issues in general, here are some steps you can perform:
+
+- First search for duplicates. If the issue is duplicate, close it by commenting with “Duplicate of #” and add any relevant new details to the existing issue.
+- If the issue is missing labels, add some to better categorize it (requires proper permissions).
+- If the title doesn’t communicate the issue, edit it for clarity (requires proper permissions).
+- If it’s a bug report, test to confirm the report or add the `Needs Testing` label. If there is not enough information to confirm the report, add the `[Status] Needs More Info` label and ask for the details needed.
+- Remove the `[Status] Needs More Info` if the author of the issue has responded with enough details.
+- Close the issue with a note if it has a `[Status] Needs More Info` label but the author didn't respond in 2+ weeks.
+- If there was conversation on the issue but no actionable steps identified, follow up with the participants to see what’s actionable.
+- If you feel comfortable triaging the issue further, then you can also:
+ - Check that the bug report is valid by debugging it to see if you can track down the technical specifics.
+ - Check if the issue is missing some detail and see if you can fill in those details. For instance, if a bug report is missing visual detail, it’s helpful to reproduce the issue locally and upload a screenshot or GIF.
+
+For triaging there are some labels which are very useful:
+- `Needs Technical Feedback` - you can apply them when you see new features or API changes proposed
+- `Needs More Info` - when it’s not clear what the issue is or it would help to provide additional details
+- `Needs Testing` - it’s useful for old bugs where it seems like they are no longer relevant
+
+## Pull Requests
+
+Gutenberg follows a feature branch pull request workflow for all code and documentation changes. At a high-level, the process looks like this:
+
+1. Check out a new feature branch locally.
+2. Make your changes, testing thoroughly.
+3. Commit your changes when you’re happy with them, and push the branch.
+4. Open your pull request.
+
+Along with this process, there are a few important points to mention:
+
+- Non-trivial pull requests should be preceded by a related issue that defines the problem to solve and allows for discussion of the most appropriate solution before actually writing code.
+- To make it far easier to merge your code, each pull request should only contain one conceptual change. Keeping contributions atomic keeps the pull request discussion focused on one topic and makes it possible to approve changes on a case-by-case basis.
+- Separate pull requests can address different items or todos from their linked issue, there’s no need for a single pull request to cover a single issue if the issue is non-trivial.
+
+### Code Review
+
+Every pull request goes through a manual code review, in addition to automated tests. The objectives for the code review are best thought of as:
+
+- Correct — Does the change do what it’s supposed to?
+- Secure — Would a nefarious party find some way to exploit this change?
+- Readable — Will your future self be able to understand this change months down the road?
+- Elegant — Does the change fit aesthetically within the overall style and architecture?
+- Altruistic — How does this change contribute to the greater whole?
+
+*As a reviewer*, your feedback should be focused on the idea, not the person. Seek to understand, be respectful, and focus on constructive dialog.
+
+*As a contributor*, your responsibility is to learn from suggestions and iterate your pull request should it be needed based on feedback. Seek to collaborate and produce the best possible contribution to the greater whole.
+
+Code reviews are encouraged by everyone who is willing to attempt one. If you review a pull request and are confident in the changes, approve it. If you don't feel totally confident it is ready for merging, add your review with a comment that says it should have another set of eyes on it before final approval. This can help filter out obvious bugs and simplify reviews for core members. Following the later reviews will also help improve your reviewing confidence in the future.
+
+If you are not yet comfortable leaving a full review, try commenting on a PR. Questions about functionality or the reasoning behind a change are helpful too. You could also comment on changes to parts of the code you understand, without leaving a full review.
+
+### Design Review
+
+If your pull request impacts the design, you should ask for a design review. To request a design review add the [Needs Design Feedback](https://github.com/WordPress/gutenberg/labels/Needs%20Design%20Feedback) label to your PR. As a guide, changes that should be reviewed:
+
+- A change based on a previous design, to confirm the design is still valid with the change.
+- Anything that changes something visually.
+- If you just want design feedback on an idea or exploration.
+
+### Merging Pull Requests
+
+A pull request can generally be merged once it is:
+
+- Deemed a worthwhile change to the codebase.
+- In compliance with all relevant code review criteria.
+- Covered by sufficient tests, as necessary.
+- Vetted against all potential edge cases.
+- Changelog entries were properly added.
+- Reviewed by someone other than the original author.
+- [Rebased](/docs/contributors/git-workflow.md#keeping-your-branch-up-to-date) onto the latest version of the master branch.
+
+The final pull request merge decision is made by the **@wordpress/gutenberg-core** team.
+
+All members of the WordPress organization on GitHub have the ability to review and merge pull requests. If you have reviewed a PR and are confident in the code, approve the pull request and comment pinging **@wordpress/gutenberg-core** or a specific core member who has been involved in the PR. Once they confirm there are no objections, you are free to merge the PR into master.
+
+Most pull requests will be automatically assigned a release milestone, but please make sure your merged pull request was assigned one. Doing so creates the historical legacy of what code landed when, and makes it possible for all project contributors (even non-technical ones) to access this information.
+
+
+### Closing Pull Requests
+
+Sometimes, a pull request may not be mergeable, no matter how much additional effort is applied to it (e.g. out of scope). In these cases, it’s best to communicate with the contributor graciously while describing why the pull request was closed, this encourages productive future involvement.
+
+Make sure to:
+
+1. Thank the contributor for their time and effort.
+2. Fully explain the reasoning behind the decision to close the pull request.
+3. Link to as much supporting documentation as possible.
+
+If you’d like a template to follow:
+
+> Thanks ____ for the time you’ve spent on this pull request.
+>
+> I’m closing this pull request because ____. To clarify further, ____.
+>
+> For more details, please see ____ and ____.
+
+## Teams
+
+Two GitHub teams are used in the project.
+
+* [Gutenberg Core](https://github.com/orgs/WordPress/teams/gutenberg-core): A team composed of people that are actively involved in the project: attending meetings regularly, participating in triage sessions, performing reviews regularly, working on features and bug fixes and performing plugin and npm releases.
+
+* [Gutenberg](https://github.com/orgs/WordPress/teams/gutenberg): A team composed of contributors with at least 2–3 meaningful contributions to the project.
+
+If you meet this criteria of several meaningful contributions having been accepted into the repository and would like to be added to the Gutenberg team, feel free to ask in the [#core-editor Slack channel](https://make.wordpress.org/chat/).
+
+## Projects
+
+We use [GitHub projects](https://github.com/WordPress/gutenberg/projects) to keep track of details that aren't immediately actionable, but that we want to keep around for future reference.
+
+Some key projects include:
+
+* [Phase 2](https://github.com/WordPress/gutenberg/projects/13) - Development tasks needed for Phase 2 of Gutenberg.
+* [Phase 2 design](https://github.com/WordPress/gutenberg/projects/21) - Tasks for design in Phase 2. Note: specific projects may have their own boards.
+* [Ideas](https://github.com/WordPress/gutenberg/projects/8) - Project containing tickets that, while closed for the time being, can be revisited in the future.
diff --git a/gb-src/docs/contributors/scripts.md b/gb-src/docs/contributors/scripts.md
new file mode 100644
index 0000000000000..28a768e41599c
--- /dev/null
+++ b/gb-src/docs/contributors/scripts.md
@@ -0,0 +1,79 @@
+# Scripts
+
+The editor provides several vendor and internal scripts to plugin developers. Script names, handles, and descriptions are documented in the table below.
+
+## WP Scripts
+
+The editor includes a number of packages to enable various pieces of functionality. Plugin developers can utilize them to create blocks, editor plugins, or generic plugins.
+
+| Script Name | Handle | Description |
+|-------------|--------|-------------|
+| [Blob](/packages/blob/README.md) | wp-blob | Blob utilities |
+| [Block Library](/packages/block-library/README.md) | wp-block-library | Block library for the editor |
+| [Blocks](/packages/blocks/README.md) | wp-blocks | Block creations |
+| [Block Serialization Default Parser](/packages/block-serialization-default-parser/README.md) | wp-block-serialization-default-parser | Default block serialization parser implementations for WordPress documents |
+| [Block Serialization Spec Parser](/packages/block-serialization-spec-parser/README.md) | wp-block-serialization-spec-parser | Grammar file (grammar.pegjs) for WordPress posts |
+| [Components](/packages/components/README.md) | wp-components | Generic components to be used for creating common UI elements |
+| [Compose](/packages/compose/README.md) | wp-compose | Collection of handy Higher Order Components (HOCs) |
+| [Core Data](/packages/core-data/README.md) | wp-core-data | Simplify access to and manipulation of core WordPress entities |
+| [Data](/packages/data/README.md) | wp-data | Data module serves as a hub to manage application state for both plugins and WordPress itself |
+| [Date](/packages/date/README.md) | wp-date | Date module for WordPress |
+| [Deprecated](/packages/deprecated/README.md) | wp-deprecated | Utility to log a message to notify developers about a deprecated feature |
+| [Dom](/packages/dom/README.md) | wp-dom | DOM utilities module for WordPress |
+| [Dom Ready](/packages/dom-ready/README.md) | wp-dom-ready | Execute callback after the DOM is loaded |
+| [Editor](/packages/editor/README.md) | wp-editor | Building blocks for WordPress editors |
+| [Edit Post](/packages/edit-post/README.md) | wp-edit-post | Edit Post Module for WordPress |
+| [Element](/packages/element/README.md) | wp-element |Element is, quite simply, an abstraction layer atop [React](https://reactjs.org/) |
+| [Escape Html](/packages/escape-html/README.md) | wp-escape-html | Escape HTML utils |
+| [Hooks](/packages/hooks/README.md) | wp-hooks | A lightweight and efficient EventManager for JavaScript |
+| [Html Entities](/packages/html-entities/README.md) | wp-html-entities | HTML entity utilities for WordPress |
+| [I18N](/packages/i18n/README.md) | wp-i18n | Internationalization utilities for client-side localization |
+| [Is Shallow Equal](/packages/is-shallow-equal/README.md) | wp-is-shallow-equal | A function for performing a shallow comparison between two objects or arrays |
+| [Keycodes](/packages/keycodes/README.md) | wp-keycodes | Keycodes utilities for WordPress, used to check the key pressed in events like `onKeyDown` |
+| [List Reusable Blocks](/packages/list-reusable-blocks/README.md) | wp-list-reusable-blocks | Package used to add import/export links to the listing page of the reusable blocks |
+| [NUX](/packages/nux/README.md) | wp-nux | Components, and wp.data methods useful for onboarding a new user to the WordPress admin interface |
+| [Plugins](/packages/plugins/README.md) | wp-plugins | Plugins module for WordPress |
+| [Redux Routine](/packages/redux-routine/README.md) | wp-redux-routine | Redux middleware for generator coroutines |
+| [Rich Text](/packages/rich-text/README.md) | wp-rich-text | Helper functions to convert HTML or a DOM tree into a rich text value and back |
+| [Shortcode](/packages/shortcode/README.md) | wp-shortcode | Shortcode module for WordPress |
+| [Token List](/packages/token-list/README.md) | wp-token-list | Constructable, plain JavaScript [DOMTokenList](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList) implementation, supporting non-browser runtimes |
+| [URL](/packages/url/README.md) | wp-url | A collection of utilities to manipulate URLs |
+| [Viewport](/packages/viewport/README.md) | wp-viewport | Module for responding to changes in the browser viewport size |
+| [Wordcount](/packages/wordcount/README.md) | wp-wordcount | WordPress word count utility |
+
+## Vendor Scripts
+
+The editor also uses some popular third-party packages and scripts. Plugin developers can use these scripts as well without bundling them in their code (and increasing file sizes).
+
+| Script Name | Handle | Description |
+|-------------|--------|-------------|
+| [React](https://reactjs.org) | react | React is a JavaScript library for building user interfaces |
+| [React Dom](https://reactjs.org/docs/react-dom.html) | react-dom | Serves as the entry point to the DOM and server renderers for React, intended to be paired with React |
+| [Moment](https://momentjs.com/) | moment| Parse, validate, manipulate, and display dates and times in JavaScript |
+| [Lodash](https://lodash.com) | lodash| Lodash is a JavaScript library which provides utility functions for common programming tasks |
+
+## Polyfill Scripts
+
+The editor also provides polyfills for certain features that may not be available in all modern browsers.
+It is recommended to use the main `wp-polyfill` script handle which takes care of loading all the below mentioned polyfills.
+
+| Script Name | Handle | Description |
+|-------------|--------|-------------|
+| [Babel Polyfill](https://babeljs.io/docs/en/babel-polyfill) | wp-polyfill | Emulate a full ES2015+ environment. Main script to load all the below mentioned additional polyfills |
+| [Fetch Polyfill](https://www.npmjs.com/package/whatwg-fetch) | wp-polyfill-fetch | Polyfill that implements a subset of the standard Fetch specification |
+| [Promise Polyfill](https://www.npmjs.com/package/promise-polyfill) | wp-polyfill-promise| Lightweight ES6 Promise polyfill for the browser and node |
+| [Formdata Polyfill](https://www.npmjs.com/package/formdata-polyfill) | wp-polyfill-formdata| Polyfill conditionally replaces the native implementation |
+| [Node Contains Polyfill](https://polyfill.io) | wp-polyfill-node-contains |Polyfill for Node.contains |
+| [Element Closest Polyfill](https://www.npmjs.com/package/element-closest) | wp-polyfill-element-closest| Return the closest element matching a selector up the DOM tree |
+
+## Bundling and code sharing
+
+When using a JavaScript bundler like [webpack](https://webpack.js.org/), the scripts mentioned here
+can be excluded from the bundle and provided by WordPress in the form of script dependencies [(see
+`wp_enqueue_script`)][https://developer.wordpress.org/reference/functions/wp_enqueue_script/#default-scripts-included-and-registered-by-wordpress].
+
+The
+[`@wordpress/dependency-extraction-webpack-plugin`](https://github.com/WordPress/gutenberg/tree/master/packages/dependency-extraction-webpack-plugin)
+provides a webpack plugin to help extract WordPress dependencies from bundles. `@wordpress/scripts`
+[`build`](https://github.com/WordPress/gutenberg/tree/master/packages/scripts#build) script includes
+the plugin by default.
diff --git a/gb-src/docs/contributors/testing-overview.md b/gb-src/docs/contributors/testing-overview.md
new file mode 100644
index 0000000000000..ef2fab465927d
--- /dev/null
+++ b/gb-src/docs/contributors/testing-overview.md
@@ -0,0 +1,388 @@
+# Testing Overview
+
+Gutenberg contains both PHP and JavaScript code, and encourages testing and code style linting for both.
+
+## Why test?
+
+Aside from the joy testing will bring to your life, tests are important not only because they help to ensure that our application behaves as it should, but also because they provide concise examples of how to use a piece of code.
+
+Tests are also part of our code base, which means we apply to them the same standards we apply to all our application code.
+
+As with all code, tests have to be maintained. Writing tests for the sake of having a test isn't the goal – rather we should try to strike the right balance between covering expected and unexpected behaviours, speedy execution and code maintenance.
+
+When writing tests consider the following:
+
+* What behaviour(s) are we testing?
+* What errors are likely to occur when we run this code?
+* Does the test test what we think it is testing? Or are we introducing false positives/negatives?
+* Is it readable? Will other contributors be able to understand how our code behaves by looking at its corresponding test?
+
+## JavaScript Testing
+
+Tests for JavaScript use [Jest](https://jestjs.io/) as the test runner and its API for [globals](https://jestjs.io/docs/en/api.html) (`describe`, `test`, `beforeEach` and so on) [assertions](https://jestjs.io/docs/en/expect.html), [mocks](https://jestjs.io/docs/en/mock-functions.html), [spies](https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname) and [mock functions](https://jestjs.io/docs/en/mock-function-api.html). If needed, you can also use [Enzyme](https://github.com/airbnb/enzyme) for React component testing.
+
+Assuming you've followed the [instructions](/docs/contributors/getting-started.md) to install Node and project dependencies, tests can be run from the command-line with NPM:
+
+```
+npm test
+```
+
+Code style in JavaScript is enforced using [ESLint](http://eslint.org/). The above `npm test` will execute both unit tests and code linting. Code linting can be verified independently by running `npm run lint`. ESLint can also fix not all, but many issues automatically by running `npm run lint:fix`. To reduce the likelihood of unexpected build failures caused by code styling issues, you're encouraged to [install an ESLint integration for your editor](https://eslint.org/docs/user-guide/integrations) and/or create a [git pre-commit hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) containing the `npm run lint:fix` command.
+
+To run unit tests only, without the linter, use `npm run test-unit` instead.
+
+### Folder structure
+
+Keep your tests in a `test` folder in your working directory. The test file should have the same name as the test subject file.
+
+```
++-- test
+| +-- bar.js
++-- bar.js
+```
+
+Only test files (with at least one test case) should live directly under `/test`. If you need to add external mocks or fixtures, place them in a sub folder, for example:
+
+* `test/mocks/[file-name].js`
+* `test/fixtures/[file-name].js`
+
+### Importing tests
+
+Given the previous folder structure, try to use relative paths when importing of the __code you're testing__, as opposed to using project paths.
+
+**Good**
+
+`import { bar } from '../bar';`
+
+**Not so good**
+
+`import { bar } from 'components/foo/bar';`
+
+It will make your life easier should you decide to relocate your code to another position in the application directory.
+
+### Describing tests
+
+Use a `describe` block to group test cases. Each test case should ideally describe one behaviour only.
+
+In test cases, try to describe in plain words the expected behaviour. For UI components, this might entail describing expected behaviour from a user perspective rather than explaining code internals.
+
+**Good**
+
+```javascript
+describe( 'CheckboxWithLabel', () => {
+ test( 'checking checkbox should disable the form submit button', () => {
+ ...
+ } );
+} );
+```
+
+**Not so good**
+
+```javascript
+describe( 'CheckboxWithLabel', () => {
+ test( 'checking checkbox should set this.state.disableButton to `true`', () => {
+ ...
+ } );
+} );
+```
+
+### Setup and Teardown methods
+
+The Jest API includes some nifty [setup and teardown methods](https://jestjs.io/docs/en/setup-teardown.html) that allow you to perform tasks *before* and *after* each or all of your tests, or tests within a specific `describe` block.
+
+These methods can handle asynchronous code to allow setup that you normally cannot do inline. As with [individual test cases](https://jestjs.io/docs/en/asynchronous.html#promises), you can return a Promise and Jest will wait for it to resolve:
+
+```javascript
+// one-time setup for *all* tests
+beforeAll( () => someAsyncAction().then( resp => {
+ window.someGlobal = resp;
+} ) );
+
+// one-time teardown for *all* tests
+afterAll( () => {
+ window.someGlobal = null;
+} );
+```
+
+`afterEach` and `afterAll` provide a perfect (and preferred) way to 'clean up' after our tests, for example, by resetting state data.
+
+Avoid placing clean up code after assertions since, if any of those tests fail, the clean up won't take place and may cause failures in unrelated tests.
+
+### Mocking dependencies
+
+#### Dependency injection
+
+Passing dependencies to a function as arguments can often make your code simpler to test. Where possible, avoid referencing dependencies in a higher scope.
+
+**Not so good**
+
+```javascript
+import VALID_VALUES_LIST from './constants'
+
+function isValueValid( value ) {
+ return VALID_VALUES_LIST.includes( value );
+}
+```
+
+Here we'd have to import and use a value from `VALID_VALUES_LIST` in order to pass:
+
+`expect( isValueValid( VALID_VALUES_LIST[ 0 ] ) ).toBe( true );`
+
+The above assertion is testing two behaviours: 1) that the function can detect an item in a list, and 2) that it can detect an item in `VALID_VALUES_LIST`.
+
+But what if we don't care what's stored in `VALID_VALUES_LIST`, or if the list is fetched via an HTTP request, and we only want to test whether `isValueValid` can detect an item in a list?
+
+**Good**
+
+```javascript
+function isValueValid( value, validValuesList = [] ) {
+ return validValuesList.includes( value );
+}
+```
+
+Because we're passing the list as an argument, we can pass mock `validValuesList` values in our tests and, as a bonus, test a few more scenarios:
+
+`expect( isValueValid( 'hulk', [ 'batman', 'superman' ] ) ).toBe( false );`
+
+`expect( isValueValid( 'hulk', null ) ).toBe( false );`
+
+`expect( isValueValid( 'hulk', [] ) ).toBe( false );`
+
+`expect( isValueValid( 'hulk', [ 'iron man', 'hulk' ] ) ).toBe( true );`
+
+#### Imported dependencies
+
+Often our code will use methods and properties from imported external and internal libraries in multiple places, which makes passing around arguments messy and impracticable. For these cases `jest.mock` offers a neat way to stub these dependencies.
+
+For instance, lets assume we have `config` module to control a great deal of functionality via feature flags.
+
+```javascript
+// bilbo.js
+import config from 'config';
+export const isBilboVisible = () => config.isEnabled( 'the-ring' ) ? false : true;
+```
+
+To test the behaviour under each condition, we stub the config object and use a jest mocking function to control the return value of `isEnabled`.
+
+```javascript
+// test/bilbo.js
+import { isEnabled } from 'config';
+import { isBilboVisible } from '../bilbo';
+
+jest.mock( 'config', () => ( {
+ // bilbo is visible by default
+ isEnabled: jest.fn( () => false ),
+} ) );
+
+describe( 'The bilbo module', () => {
+ test( 'bilbo should be visible by default', () => {
+ expect( isBilboVisible() ).toBe( true );
+ } );
+
+ test( 'bilbo should be invisible when the `the-ring` config feature flag is enabled', () => {
+ isEnabled.mockImplementationOnce( name => name === 'the-ring' );
+ expect( isBilboVisible() ).toBe( false );
+ } );
+} );
+```
+
+### Testing globals
+
+We can use [Jest spies](https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname) to test code that calls global methods.
+
+```javascript
+import { myModuleFunctionThatOpensANewWindow } from '../my-module';
+
+describe( 'my module', () => {
+ beforeAll( () => {
+ jest.spyOn( global, 'open' )
+ .mockImplementation( () => true );
+ } );
+
+ test( 'something', () => {
+ myModuleFunctionThatOpensANewWindow();
+ expect( global.open ).toHaveBeenCalled();
+ } );
+} );
+```
+
+### Snapshot testing
+
+This is an overview of [snapshot testing] and how to best leverage snapshot tests.
+
+#### TL;DR Broken snapshots
+
+When a snapshot test fails, it just means that a component's rendering has changed. If that was unintended, then the snapshot test just prevented a bug 😊
+
+However, if the change was intentional, follow these steps to update the snapshot. Run the following to update the snapshots:
+ ```sh
+ # --testPathPattern is optional but will be much faster by only running matching tests
+ npm run test-unit -- --updateSnapshot --testPathPattern path/to/tests
+ ```
+1. Review the diff and ensure the changes are expected and intentional.
+2. Commit.
+
+#### What are snapshots?
+
+Snapshots are just a representation of some data structure generated by tests. Snapshots are stored in files and committed alongside the tests. When the tests are run, the data structure generated is compared with the snapshot on file.
+
+It's very easy to make a snapshot:
+
+```js
+test( 'foobar test', () => {
+ const foobar = { foo: 'bar' };
+
+ expect( foobar ).toMatchSnapshot();
+} );
+```
+
+This is the produced snapshot:
+
+```js
+exports[`test foobar test 1`] = `
+ Object {
+ "foo": "bar",
+ }
+`;
+```
+
+You should never create or modify a snapshot directly, they are generated and updated by tests.
+
+#### Advantages
+
+* Trivial and concise to add tests.
+* Protect against unintentional changes.
+* Simple to work with.
+* Reveal internal structures without running the application.
+
+#### Disadvantages
+
+* Not expressive.
+* Only catch issues when changes are introduced.
+* Are problematic for anything non-deterministic.
+
+#### Use cases
+
+Snapshot are mostly targeted at component testing. They make us conscious of changes to a component's structure which makes them _ideal_ for refactoring. If a snapshot is kept up to date over the course of a series of commits, the snapshot diffs record the evolution of a component's structure. Pretty cool 😎
+
+```js
+import { shallow } from 'enzyme';
+import SolarSystem from 'solar-system';
+import { Mars } from 'planets';
+
+describe( 'SolarSystem', () => {
+ test( 'should render', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper ).toMatchSnapshot();
+ } );
+
+ test( 'should contain mars if planets is true', () => {
+ const wrapper = shallow( );
+
+ expect( wrapper ).toMatchSnapshot();
+ expect( wrapper.find( Mars ) ).toHaveLength( 1 );
+ } );
+} );
+```
+
+Reducer tests are also a great fit for snapshots. They are often large, complex data structures that shouldn't change unexpectedly, exactly what snapshots excel at!
+
+#### Working with snapshots
+
+You might be blindsided by CI tests failing when snapshots don't match. You'll need to [update snapshots] if the changes are expected. The quick and dirty solution is to invoke Jest with `--updateSnapshot`. That can be done as follows:
+
+```sh
+npm run test-unit -- --updateSnapshot --testPathPattern path/to/tests
+```
+
+`--testPathPattern` is not required, but specifying a path will speed things up by running a subset of tests.
+
+It's a great idea to keep `npm run test-unit:watch` running in the background as you work. Jest will run only the relevant tests for changed files, and when snapshot tests fail, just hit `u` to update a snapshot!
+
+#### Pain points
+
+Non-deterministic tests may not make consistent snapshots, so beware. When working with anything random, time-based, or otherwise non-deterministic, snapshots will be problematic.
+
+Connected components are tricky to work with. To snapshot a connected component you'll probably want to export the unconnected component:
+
+```js
+// my-component.js
+export { MyComponent };
+export default connect( mapStateToProps )( MyComponent );
+
+// test/my-component.js
+import { MyComponent } from '..';
+// run those MyComponent tests…
+```
+
+The connected props will need need to be manually provided. This is a good opportunity to audit the connected state.
+
+#### Best practices
+
+If you're starting a refactor, snapshots are quite nice, you can add them as the first commit on a branch and watch as they evolve.
+
+Snapshots themselves don't express anything about what we expect. Snapshots are best used in conjunction with other tests that describe our expectations, like in the example above:
+
+```js
+test( 'should contain mars if planets is true', () => {
+ const wrapper = shallow( );
+
+ // Snapshot will catch unintended changes
+ expect( wrapper ).toMatchSnapshot();
+
+ // This is what we actually expect to find in our test
+ expect( wrapper.find( Mars ) ).toHaveLength( 1 );
+} );
+```
+
+[`shallow`](http://airbnb.io/enzyme/docs/api/shallow.html) rendering is your friend:
+
+> Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components.
+
+It's tempting to snapshot deep renders, but that makes for huge snapshots. Additionally, deep renders no longer test a single component, but an entire tree. With `shallow`, we snapshot just the components that are directly rendered by the component we want to test.
+
+## End to end Testing
+
+If you're using the built-in [local environment](/docs/contributors/getting-started.md#local-environment), you can run the e2e tests locally using this command:
+
+```bash
+npm run test-e2e
+```
+
+or interactively
+
+```bash
+npm run test-e2e:watch
+```
+
+Sometimes it's useful to observe the browser while running tests. To do so you can use these environment variables:
+
+```bash
+PUPPETEER_HEADLESS=false PUPPETEER_SLOWMO=80 npm run test-e2e:watch
+```
+
+If you're using a different setup, you can provide the base URL, username and password like this:
+
+```bash
+WP_BASE_URL=http://localhost:8888 WP_USERNAME=admin WP_PASSWORD=password npm run test-e2e
+```
+
+### Core Block Testing
+
+Every core block is required to have at least one set of fixture files for its main save function and one for each deprecation. These fixtures test the parsing and serialization of the block. See [the e2e tests fixtures readme](/packages/e2e-tests/fixtures/blocks/README.md) for more information and instructions.
+
+## PHP Testing
+
+Tests for PHP use [PHPUnit](https://phpunit.de/) as the testing framework. If you're using the built-in [local environment](/docs/contributors/getting-started.md#local-environment), you can run the PHP tests locally using this command:
+
+```bash
+npm run test-php
+```
+
+Code style in PHP is enforced using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). It is recommended that you install PHP_CodeSniffer and the [WordPress Coding Standards for PHP_CodeSniffer](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards#installation) ruleset using [Composer](https://getcomposer.org/). With Composer installed, run `composer install` from the project directory to install dependencies. The above `npm run test-php` will execute both unit tests and code linting. Code linting can be verified independently by running `npm run lint-php`.
+
+To run unit tests only, without the linter, use `npm run test-unit-php` instead.
+
+[snapshot testing]: https://jestjs.io/docs/en/snapshot-testing.html
+[update snapshots]: https://jestjs.io/docs/en/snapshot-testing.html#updating-snapshots
diff --git a/gb-src/docs/designers-developers/assets/fancy-quote-in-inspector.png b/gb-src/docs/designers-developers/assets/fancy-quote-in-inspector.png
new file mode 100644
index 0000000000000..6bd8c06a9e397
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/fancy-quote-in-inspector.png differ
diff --git a/gb-src/docs/designers-developers/assets/fancy-quote-with-style.png b/gb-src/docs/designers-developers/assets/fancy-quote-with-style.png
new file mode 100644
index 0000000000000..31f38063a1f1d
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/fancy-quote-with-style.png differ
diff --git a/gb-src/docs/designers-developers/assets/inspector.png b/gb-src/docs/designers-developers/assets/inspector.png
new file mode 100644
index 0000000000000..9f143bdbcbfc7
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/inspector.png differ
diff --git a/gb-src/docs/designers-developers/assets/js-tutorial-console-log-error.png b/gb-src/docs/designers-developers/assets/js-tutorial-console-log-error.png
new file mode 100644
index 0000000000000..836a663484192
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/js-tutorial-console-log-error.png differ
diff --git a/gb-src/docs/designers-developers/assets/js-tutorial-console-log-success.png b/gb-src/docs/designers-developers/assets/js-tutorial-console-log-success.png
new file mode 100644
index 0000000000000..7b42853fb4064
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/js-tutorial-console-log-success.png differ
diff --git a/gb-src/docs/designers-developers/assets/js-tutorial-error-blocks-undefined.png b/gb-src/docs/designers-developers/assets/js-tutorial-error-blocks-undefined.png
new file mode 100644
index 0000000000000..1f27c36ce7595
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/js-tutorial-error-blocks-undefined.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-block-settings-menu-item-screenshot.png b/gb-src/docs/designers-developers/assets/plugin-block-settings-menu-item-screenshot.png
new file mode 100644
index 0000000000000..bc33b4fd205a6
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-block-settings-menu-item-screenshot.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-more-menu-item.png b/gb-src/docs/designers-developers/assets/plugin-more-menu-item.png
new file mode 100644
index 0000000000000..23fa73db266a2
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-more-menu-item.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-post-publish-panel.png b/gb-src/docs/designers-developers/assets/plugin-post-publish-panel.png
new file mode 100644
index 0000000000000..b4cd9318b68aa
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-post-publish-panel.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-post-status-info-location.png b/gb-src/docs/designers-developers/assets/plugin-post-status-info-location.png
new file mode 100644
index 0000000000000..fa35405e7a099
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-post-status-info-location.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-pre-publish-panel.png b/gb-src/docs/designers-developers/assets/plugin-pre-publish-panel.png
new file mode 100644
index 0000000000000..ea765f4f54319
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-pre-publish-panel.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-sidebar-closed-state.png b/gb-src/docs/designers-developers/assets/plugin-sidebar-closed-state.png
new file mode 100644
index 0000000000000..025da900ffcdd
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-sidebar-closed-state.png differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-sidebar-more-menu-item.gif b/gb-src/docs/designers-developers/assets/plugin-sidebar-more-menu-item.gif
new file mode 100644
index 0000000000000..851898484bc3c
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-sidebar-more-menu-item.gif differ
diff --git a/gb-src/docs/designers-developers/assets/plugin-sidebar-open-state.png b/gb-src/docs/designers-developers/assets/plugin-sidebar-open-state.png
new file mode 100644
index 0000000000000..f1c3781a500f0
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/plugin-sidebar-open-state.png differ
diff --git a/gb-src/docs/designers-developers/assets/sidebar-style-and-controls.png b/gb-src/docs/designers-developers/assets/sidebar-style-and-controls.png
new file mode 100644
index 0000000000000..725ddfdd87a4b
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/sidebar-style-and-controls.png differ
diff --git a/gb-src/docs/designers-developers/assets/sidebar-up-and-running.png b/gb-src/docs/designers-developers/assets/sidebar-up-and-running.png
new file mode 100644
index 0000000000000..12bc2947d48a3
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/sidebar-up-and-running.png differ
diff --git a/gb-src/docs/designers-developers/assets/toolbar-text.png b/gb-src/docs/designers-developers/assets/toolbar-text.png
new file mode 100644
index 0000000000000..8dbf503d50391
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/toolbar-text.png differ
diff --git a/gb-src/docs/designers-developers/assets/toolbar-with-custom-button.png b/gb-src/docs/designers-developers/assets/toolbar-with-custom-button.png
new file mode 100644
index 0000000000000..3b22afaca318a
Binary files /dev/null and b/gb-src/docs/designers-developers/assets/toolbar-with-custom-button.png differ
diff --git a/gb-src/docs/designers-developers/designers/README.md b/gb-src/docs/designers-developers/designers/README.md
new file mode 100644
index 0000000000000..a630847b37661
--- /dev/null
+++ b/gb-src/docs/designers-developers/designers/README.md
@@ -0,0 +1,3 @@
+# Designer Documentation
+
+For those designing blocks and other block editor integrations, this documentation will provide resources for creating beautiful and intuitive layouts.
diff --git a/gb-src/docs/designers-developers/designers/animation.md b/gb-src/docs/designers-developers/designers/animation.md
new file mode 100644
index 0000000000000..24948723e0d8c
--- /dev/null
+++ b/gb-src/docs/designers-developers/designers/animation.md
@@ -0,0 +1,40 @@
+# Animation
+
+Animation can help reinforce a sense of hierarchy and spatial orientation. This document goes into principles you should follow when you add animation.
+
+## Principles
+
+### Point of Origin
+
+- Animation can help anchor an interface element. For example a menu can scale up from the button that opened it.
+- Animation can help give a sense of place; for example a sidebar can animate in from the side, implying it was always hidden off-screen.
+- Design your animations as if you're working with real-world materials. Imagine your user interface elements are made of real materials — when not on screen, where are they? Use animation to help express that.
+
+### Speed
+
+- Animations should never block a user interaction. They should be fast, almost always complete in less than 0.2 seconds.
+- A user should not have to wait for an animation to finish before they can interact.
+- Animations should be performant. Use `transform` CSS properties when you can, these render elements on the GPU, making them smooth.
+- If an animation can't be made fast & performant, leave it out.
+
+### Simple
+
+- Don't bounce if the material isn't made of rubber.
+- Don't rotate, fold, or animate on a curved path. Keep it simple.
+
+### Consistency
+
+In creating consistent animations, we have to establish physical rules for how elements behave when animated. When all animations follow these rules, they feel consistent, related, and predictable. An animation should match user expectations, if it doesn't, it's probably not the right animation for the job.
+
+Reuse animations if one already exists for your task.
+
+## Accessibility Considerations
+
+- Animations should be subtle. Be cognizent of users with [vestibular disorders triggered by motion](https://www.ncbi.nlm.nih.gov/pubmed/29017000).
+- Don't animate elements that are currently reporting content to adaptive technology (e.g., an `aria-live` region that's receiving updates). This can cause confusion wherein the technology tries to parse a region that's actively changing.
+- Avoid animations that aren't directly triggered by user behaviors.
+- Whenever possible, ensure that animations respect the OS-level "Reduce Motion" settings. This can be done by utilizing the [`prefers-reduce-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) media query. Gutenberg includes a `@reduce-motion` mixin for this, to be used alongside rules that include a CSS `animate` property.
+
+## Inventory of Reused Animations
+
+The generic `Animate` component is used to animate different parts of the interface. See [the component documentation](/packages/components/src/animate/README.md) for more details about the available animations.
diff --git a/gb-src/docs/designers-developers/designers/assets/advanced-settings-do.png b/gb-src/docs/designers-developers/designers/assets/advanced-settings-do.png
new file mode 100644
index 0000000000000..c589ace4c1348
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/advanced-settings-do.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/block-controls-do.png b/gb-src/docs/designers-developers/designers/assets/block-controls-do.png
new file mode 100644
index 0000000000000..86f2019638fe0
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/block-controls-do.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/block-controls-dont.png b/gb-src/docs/designers-developers/designers/assets/block-controls-dont.png
new file mode 100644
index 0000000000000..301a521154641
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/block-controls-dont.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/block-descriptions-do.png b/gb-src/docs/designers-developers/designers/assets/block-descriptions-do.png
new file mode 100644
index 0000000000000..53d6a443c7890
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/block-descriptions-do.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/block-descriptions-dont.png b/gb-src/docs/designers-developers/designers/assets/block-descriptions-dont.png
new file mode 100644
index 0000000000000..9225cb5d8575a
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/block-descriptions-dont.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/blocks-do.png b/gb-src/docs/designers-developers/designers/assets/blocks-do.png
new file mode 100644
index 0000000000000..bd79797dfea12
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/blocks-do.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/blocks-dont.png b/gb-src/docs/designers-developers/designers/assets/blocks-dont.png
new file mode 100644
index 0000000000000..2c69f1ada0921
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/blocks-dont.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/placeholder-do.png b/gb-src/docs/designers-developers/designers/assets/placeholder-do.png
new file mode 100644
index 0000000000000..dc5f1327d429a
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/placeholder-do.png differ
diff --git a/gb-src/docs/designers-developers/designers/assets/placeholder-dont.png b/gb-src/docs/designers-developers/designers/assets/placeholder-dont.png
new file mode 100644
index 0000000000000..4f313ccee5ac1
Binary files /dev/null and b/gb-src/docs/designers-developers/designers/assets/placeholder-dont.png differ
diff --git a/gb-src/docs/designers-developers/designers/block-design.md b/gb-src/docs/designers-developers/designers/block-design.md
new file mode 100644
index 0000000000000..7ee481637bb22
--- /dev/null
+++ b/gb-src/docs/designers-developers/designers/block-design.md
@@ -0,0 +1,203 @@
+# Block Design
+
+The following are best practices for designing a new block, with recommendations and detailed descriptions of existing blocks to illustrate our approach to creating blocks.
+
+## Best Practices
+
+### The primary interface for a block is the content area of the block
+
+Since the block itself represents what will actually appear on the site, interaction here hews closest to the principle of direct manipulation and will be most intuitive to the user. This should be thought of as the primary interface for adding and manipulating content and adjusting how it is displayed. There are two ways of interacting here:
+
+1. The placeholder content in the content area of the block can be thought of as a guide or interface for users to follow a set of instructions or “fill in the blanks”. For example, a block that embeds content from a 3rd-party service might contain controls for signing in to that service in the placeholder.
+2. After the user has added content, selecting the block can reveal additional controls to adjust or edit that content. For example, a signup block might reveal a control for hiding/showing subscriber count. However, this should be done in minimal ways, so as to avoid dramatically changing the size and display of a block when a user selects it (this could be disorienting or annoying).
+
+### The Block Toolbar is a secondary place for required options & controls
+
+Basic block settings won’t always make sense in the context of the placeholder/content UI. As a secondary option, options that are critical to the functionality of a block can live in the block toolbar. The Block Toolbar is still highly contextual and visible on all screen sizes. One notable constraint with the Block Toolbar is that it is icon-based UI, so any controls that live in the Block Toolbar need to be ones that can effectively be communicated via an icon or icon group.
+
+### The Settings Sidebar should only be used for advanced, tertiary controls
+
+The Settings Sidebar is not visible by default on a small / mobile screen, and may also be collapsed in a desktop view. Therefore, it should not be relied on for anything that is necessary for the basic operation of the block. Pick good defaults, make important actions available in the block toolbar, and think of the Settings Sidebar as something that most users should not need to open.
+
+In addition, use sections and headers in the Settings Sidebar if there are more than a handful of options, in order to allow users to easily scan and understand the options available.
+
+Each Settings Sidebar comes with an "Advanced" section by default. This area houses an "Additional CSS Class" field, and should be used to house other power user controls.
+
+## Setup state vs. live preview state
+
+Setup states, sometimes referred to as "placeholders", can be used to walk users through an initial process before showing the live preview state of the block. The setup process gathers information from the user that is needed to render the block. A block’s setup state is indicated with a grey background to provide clear differentiation for the user. Not all blocks have setup states — for example, the Paragraph block.
+
+
+
+A setup state is **not** necessary if:
+
+- You can provide good default content in the block that will meet most people’s needs.
+- That default content is easy to edit and customize.
+
+Use a setup state if:
+
+- There isn’t a clear default state that would work for most users.
+- You need to gather input from the user that doesn’t have a 1-1 relationship with the live preview of the block (for example, if you need the user to input an API key to render content).
+- You need more information from the user in order to render useful default content.
+
+For blocks that do have setup states, once the user has gone through the setup process, the placeholder is replaced with the live preview state of that block.
+
+
+
+When the block is selected, additional controls may be revealed to customize the block’s contents. For example, when the image gallery is selected, it reveals controls to remove or add images.
+
+
+
+In most cases, a block’s setup state is only shown once and then further customization is done via the live preview state. However, in some cases it might be desirable to allow the user to return to the setup state — for example, if all the block content has been deleted or via a link from the block’s toolbar or sidebar.
+
+## Do's and Don'ts
+
+### Block Identification
+
+A block should have a straightforward, short name so users can easily find it in the Block Library. A block named "YouTube" is easy to find and understand. The same block, named "Embedded Video (YouTube)", would be less clear and harder to find in the Block Library.
+
+When referring to a block in documentation or UI, use title case for the block title, and lowercase for the "block" descriptor. For example:
+
+- Paragraph block
+- Latest Posts block
+- Media & Text block
+
+Blocks should have an identifying icon, ideally using a single color. Try to avoid using the same icon used by an existing block. The core block icons are based on [Material Design Icons](https://material.io/tools/icons/). Look to that icon set, or to [Dashicons](https://developer.wordpress.org/resource/dashicons/) for style inspiration.
+
+
+**Do:**
+Use concise block names.
+
+
+**Don't:**
+Avoid long, multi-line block names.
+
+### Block Description
+
+Every block should include a description that clearly explains the block's function. The description will display in the Settings Sidebar.
+
+You can add a description by using the description attribute in the [registerBlockType function](/docs/designers-developers/developers/block-api/block-registration/).
+
+Stick to a single imperative sentence with an action + subject format. Examples:
+
+- Start with the building block of all narrative.
+- Introduce new sections and organize content to help visitors (and search engines) understand the structure of your content.
+- Create a bulleted or numbered list.
+
+
+**Do:**
+Use a short, simple, block description.
+
+
+**Don't:**
+Avoid long descriptions and branding.
+
+### Placeholders
+
+If your block requires a user to configure some options before you can display it, you should provide an instructive placeholder state.
+
+
+**Do:**
+Provide an instructive placeholder state.
+
+
+**Don't:**
+Avoid branding and relying on the title alone to convey instructions.
+
+### Selected and Unselected States
+
+When unselected, your block should preview its content as closely to the front-end output as possible.
+
+When selected, your block may surface additional options like input fields or buttons to configure the block directly, especially when they are necessary for basic operation.
+
+
+**Do:**
+For controls that are essential the the operation of the block, provide them directly in inside the block edit view.
+
+
+**Don't:**
+Do not put controls that are essential to the block in the sidebar, or the block will appear non-functional to mobile users, or desktop users who have dismissed the sidebar.
+
+### Advanced Block Settings
+
+The “Block” tab of the Settings Sidebar can contain additional block options and configuration. Keep in mind that a user can dismiss the sidebar and never use it. You should not put critical options in the Sidebar.
+
+
+**Do:**
+Because the Drop Cap feature is not necessary for the basic operation of the block, you can put it to the Block tab as optional configuration.
+
+### Consider mobile
+
+Check how your block looks, feels, and works on as many devices and screen sizes as you can.
+
+### Support Gutenberg's dark background editor scheme
+
+Check how your block looks with [dark backgrounds](/docs/designers-developers/developers/themes/theme-support.md#dark-backgrounds) in the editor.
+
+## Examples
+
+To demonstrate some of these practices, here are a few annotated examples of default Gutenberg blocks:
+
+### Paragraph
+
+The most basic unit of the editor. The Paragraph block is a simple input field.
+
+
+
+### Placeholder:
+
+- Simple placeholder text that reads “Start writing or type / to choose a block”. The placeholder disappears when the block is selected.
+
+### Selected state:
+
+- Block Toolbar: Has a switcher to perform transformations to headings, etc.
+- Block Toolbar: Has basic text alignments
+- Block Toolbar: Has inline formatting options, bold, italic, strikethrough and link
+
+### Image
+
+Basic image block.
+
+
+
+### Placeholder:
+
+- A generic gray placeholder block with options to upload an image, drag and drop an image directly on it, or pick an image from the media library.
+
+### Selected state:
+
+- Block Toolbar: Alignments, including wide and full-width if the theme supports it.
+- Block Toolbar: Edit Image, to open the Media Library
+- Block Toolbar: Link button
+- When an image is uploaded, a caption input field appears with a “Write caption…” placeholder text below the image:
+
+
+
+### Block settings:
+
+- Has description: “They're worth 1,000 words! Insert a single image.”
+- Has options for changing or adding alt text and adding additional custom CSS classes.
+
+_Future improvements to the Image block could include getting rid of the media modal, in place of letting users select images directly from the placeholder itself. In general, try to avoid modals._
+
+### Latest Post
+
+
+
+### Placeholder:
+
+Has no placeholder, as it works immediately upon insertion. The default inserted state shows the last 5 posts.
+
+### Selected state:
+
+- Block Toolbar: Alignments
+- Block Toolbar: Options for picking list view or grid view
+
+_Note that the Block Toolbar does not include the Block Chip in this case, since there are no similar blocks to switch to._
+
+### Block settings:
+
+- Has description: “Display a list of your most recent posts.”
+- Has options for post order, narrowing the list by category, changing the default number of posts to show, and showing the post date.
+
+_Latest Posts is fully functional as soon as it’s inserted, because it comes with good defaults._
diff --git a/gb-src/docs/designers-developers/designers/design-patterns.md b/gb-src/docs/designers-developers/designers/design-patterns.md
new file mode 100644
index 0000000000000..bf06df6da5e9d
--- /dev/null
+++ b/gb-src/docs/designers-developers/designers/design-patterns.md
@@ -0,0 +1,65 @@
+# Patterns
+
+## Basic Editor Interface
+
+The block editor’s general layout uses on a bar at the top, with content below.
+
+
+
+The **Toolbar** contains document-level actions: Editor mode, save status, global actions for undo/redo/insert, the settings toggle, and publish options.
+
+The **Content Area** contains the document itself.
+
+The **Settings Sidebar** contains additional settings for the document (tags, categories, schedule etc.) and for blocks in the “Block” tab. A cog button in the toolbar hides the Settings Sidebar, allowing the user to enjoy a more immersive writing experience. On small screens, the sidebar is hidden by default.
+
+## The Block Interface
+
+The block itself is the most basic unit of the editor. Generally speaking, everything is a block. Users build posts and pages using blocks, mimicking the vertical flow of the underlying HTML markup.
+
+By surfacing each section of the document as a manipulatable block, we surface block-specific features contextually. This is inspired by desktop app conventions, and allows for a breadth of advanced features without weighing down the UI.
+
+A selected block shows a number of contextual actions:
+
+
+
+The block interface has basic actions. The block editor aims for good, common defaults, so users should be able to create a complete document without actually needing the advanced actions in the Settings Sidebar.
+
+**The Block Toolbar** highlights commonly-used actions. The **Block Chip** lives in the block toolbar, and contains high-level controls for the selected block. It primarily allows users to transform a block into another type of compatible block. Some blocks also use the block chip to users to choose from a set of alternate block styles.
+
+The **Block Formatting** options let users adjust block-level settings, and the **Inline Formatting** options allow adjustments to elements inside the block. When a block is long, the block toolbar pins itself to the top of the screen as the user scrolls down the page.
+
+Blocks can be moved up and down via the **Block Mover** icons on the left. Additional block actions are available on the right via an ellipsis menu: deleting and duplicating blocks, as well as **advanced actions** like “Edit as HTML” and “Convert to Reusable Block.”
+
+An unselected block does not show the block toolbar or any other contextual controls. In effect, an unselected block is a preview of the content itself:
+
+
+
+Please note that selection and focus can be different. An image block can be selected while the focus is on the caption field.
+
+## Settings Sidebar
+
+
+
+The sidebar has two tabs, Document and Block:
+
+- The **Document Tab** shows metadata and settings for the post or page being edited.
+- The **Block Tab** shows metadata and settings for the currently selected block.
+
+Each tab has sets of editable fields (**Sidebar Sections**) that users can toggle open or closed.
+
+If a block requires advanced configuration, those settings should live in the Settings sidebar (Editor block settings can also be reached directly by clicking the cog icon next to a block). Don’t put anything in the sidebar block tab that is necessary for the basic operation of your block; your user might dismiss the sidebar for an immersive writing experience. Pick good defaults, and make important actions available in the block toolbar.
+
+Actions that could go in the block tab of the sidebar could be:
+
+- Drop cap, for text
+- Number of columns for galleries
+- Number of posts, or category, in the “Latest Posts” block
+- Any configuration that you don’t need access to in order to perform basic tasks
+
+## Block Library
+
+
+
+The **Block Library** appears when someone inserts a block, whether via the toolbar, or contextually within the content area. Inside, blocks are organized into expandable sections. The block library’s search bar auto-filters the list of blocks as the user types. Users can choose a block by selecting the **Block Button** or the **Block Name**.
+
+**Parent Blocks** (Blocks that contain children blocks) are represented by a layered block button.
diff --git a/gb-src/docs/designers-developers/designers/design-resources.md b/gb-src/docs/designers-developers/designers/design-resources.md
new file mode 100644
index 0000000000000..eaa82d16356c3
--- /dev/null
+++ b/gb-src/docs/designers-developers/designers/design-resources.md
@@ -0,0 +1,51 @@
+# Resources
+
+## Figma
+The [WordPress Design team](https://make.wordpress.org/design/) uses [Figma](https://www.figma.com/) to collaborate and share work. If you'd like to contribute, join the [#design channel](https://app.slack.com/client/T024MFP4J/C02S78ZAL) in [Slack](https://make.wordpress.org/chat/) and ask the team to set you up with a free Figma account. This will give you access to a helpful library of components used in WordPress. They are stable, fully supported, up to date, and ready for use in designs and prototypes.
+
+### How to contribute
+
+### Resources for learning how to use Figma
+[Getting started with Figma](https://help.figma.com/category/9-getting-started)
+
+[Top Online Tutorials to Learn Figma for UI/UX Design](https://medium.com/quick-design/top-online-tutorials-to-learn-figma-for-ui-ux-design-4e9c6721a72d)
+
+[Take a Tour Around Figma](https://help.figma.com/article/12-getting-familiar-with-figma)
+
+### Learning how to use files and projects
+[Getting started with Figma files and projects](https://help.figma.com/article/298-getting-started-with-files-and-projects)
+
+[What are files?](https://help.figma.com/article/298-getting-started-with-files-and-projects#files)
+
+[What are projects?](https://help.figma.com/article/298-getting-started-with-files-and-projects#projects)
+
+[Video tutorial](https://www.youtube.com/watch?v=c5HS6smhq2E)
+
+[FAQ](https://help.figma.com/article/298-getting-started-with-files-and-projects#faq)
+
+### Learning how to use components
+[Getting started with components](https://help.figma.com/article/66-components)
+
+[What are components?](https://help.figma.com/article/66-components#components)
+
+[Video tutorial](https://help.figma.com/article/66-components#videos)
+
+### Learning how to use WordPress Figma libraries
+**How to turn on the WordPress Components library in Figma**
+
+
+
+1. Click the **Team Library** icon in the **Assets** Panel:
+
+
+
+2. The **Library** modal will open and allow you to view a list of available libraries. Toggle to _Enable_ or _Disable_ a specific library:
+
+
+
+**How to refine or contribute to the WordPress components React library _(Coming soon)_**
+
+WordPress components in Figma mirror the live React components. Documentation for how to refine or contribute to WordPress components in React is coming soon.
+
+
+If you have questions, please don’t hesitate to ask in the #design channel on the WordPress community Slack.
diff --git a/gb-src/docs/designers-developers/developers/README.md b/gb-src/docs/designers-developers/developers/README.md
new file mode 100644
index 0000000000000..ec19e12703bac
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/README.md
@@ -0,0 +1,45 @@
+# Developer Documentation
+
+The new editor is highly flexible, like most of WordPress. You can build custom blocks, modify the editor's appearance, add special plugins, and much more.
+
+## Creating Blocks
+
+The editor is about blocks, and the main extensibility API is the Block API. It allows you to create your own static blocks, [Dynamic Blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md) ( rendered on the server ) and also blocks capable of saving data to Post Meta for more structured content.
+
+If you want to learn more about block creation, the [Blocks Tutorial](/docs/designers-developers/developers/tutorials/block-tutorial/readme.md) is the best place to start.
+
+## Extending Blocks
+
+It is also possible to modify the behavior of existing blocks or even remove them completely using filters.
+
+Learn more in the [Block Filters](/docs/designers-developers/developers/filters/block-filters.md) section.
+
+## Extending the Editor UI
+
+Extending the editor UI can be accomplished with the `registerPlugin` API, allowing you to define all your plugin's UI elements in one place.
+
+Refer to the [Plugins](/packages/plugins/README.md) and [Edit Post](/packages/edit-post/README.md) section for more information.
+
+You can also filter certain aspects of the editor; this is documented on the [Editor Filters](/docs/designers-developers/developers/filters/editor-filters.md) page.
+
+## Meta Boxes
+
+Porting PHP meta boxes to blocks or sidebar plugins is highly encouraged, learn how through these [meta data tutorials](/docs/designers-developers/developers/tutorials/metabox/readme.md).
+
+See how the new editor [supports existing Meta Boxes](/docs/designers-developers/developers/backward-compatibility/meta-box.md).
+
+## Theme Support
+
+By default, blocks provide their styles to enable basic support for blocks in themes without any change. Themes can add/override these styles, or rely on defaults.
+
+There are some advanced block features which require opt-in support in the theme. See [theme support](/docs/designers-developers/developers/themes/theme-support.md).
+
+## Autocomplete
+
+Autocompleters within blocks may be extended and overridden. Learn more about the [autocomplete](/docs/designers-developers/developers/filters/autocomplete-filters.md) filters.
+
+## Block Parsing and Serialization
+
+Posts in the editor move through a couple of different stages between being stored in `post_content` and appearing in the editor. Since the blocks themselves are data structures that live in memory it takes a parsing and serialization step to transform out from and into the stored format in the database.
+
+Customizing the parser is an advanced topic that you can learn more about in the [Extending the Parser](/docs/designers-developers/developers/filters/parser-filters.md) section.
diff --git a/gb-src/docs/designers-developers/developers/accessibility.md b/gb-src/docs/designers-developers/developers/accessibility.md
new file mode 100644
index 0000000000000..ab6c0d0066b1d
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/accessibility.md
@@ -0,0 +1,17 @@
+# Accessibility
+
+Accessibility documentation for developers working on the Gutenberg Project.
+
+For more information on accessibility and WordPress see the [Make WordPress Accessibility Handbook](https://make.wordpress.org/accessibility/handbook/) and the [Accessibility Team section](https://make.wordpress.org/accessibility/).
+
+## Landmark Regions
+
+It is a best practice to include ALL content on the page in landmarks, so that screen reader users who rely on them to navigate from section to section do not lose track of content.
+
+For setting up navigation between different regions, see the [navigateRegions package](/packages/components/src/higher-order/navigate-regions/README.md) for additional documentation.
+
+Read more regarding landmark design from W3C:
+
+- [General Principles of Landmark Design](https://www.w3.org/TR/wai-aria-practices-1.1/#general-principles-of-landmark-design)
+- [ARIA Landmarks Example](https://www.w3.org/TR/wai-aria-practices/examples/landmarks/)
+- [HTML5 elements that by default define ARIA landmarks](https://www.w3.org/TR/wai-aria-practices/examples/landmarks/HTML5.html)
diff --git a/gb-src/docs/designers-developers/developers/backward-compatibility/README.md b/gb-src/docs/designers-developers/developers/backward-compatibility/README.md
new file mode 100644
index 0000000000000..bd559617c5b17
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/backward-compatibility/README.md
@@ -0,0 +1 @@
+# Backward Compatibility
diff --git a/gb-src/docs/designers-developers/developers/backward-compatibility/deprecations.md b/gb-src/docs/designers-developers/developers/backward-compatibility/deprecations.md
new file mode 100644
index 0000000000000..9421833ba0290
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/backward-compatibility/deprecations.md
@@ -0,0 +1,272 @@
+# Deprecations
+
+For features included in the Gutenberg plugin, the deprecation policy is intended to support backward compatibility for two minor plugin releases, when possible. Features and code included in a stable release of WordPress are not included in this deprecation timeline, and are instead subject to the [versioning policies of the WordPress project](https://make.wordpress.org/core/handbook/about/release-cycle/version-numbering/). The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version.
+
+## 5.5.0
+
+- The PHP function `gutenberg_init` has been removed.
+- The PHP function `is_gutenberg_page` has been removed. Use [`WP_Screen::is_block_editor`](https://developer.wordpress.org/reference/classes/wp_screen/is_block_editor/) instead.
+- The PHP function `the_gutenberg_project` has been removed.
+- The PHP function `gutenberg_default_post_format_template` has been removed.
+- The PHP function `gutenberg_get_available_image_sizes` has been removed.
+- The PHP function `gutenberg_get_autosave_newer_than_post_save` has been removed.
+- The PHP function `gutenberg_editor_scripts_and_styles` has been removed.
+
+## 5.4.0
+
+- The PHP function `gutenberg_load_plugin_textdomain` has been removed.
+- The PHP function `gutenberg_get_jed_locale_data` has been removed.
+- The PHP function `gutenberg_load_locale_data` has been removed.
+
+## 5.3.0
+
+- The PHP function `gutenberg_redirect_to_classic_editor_when_saving_posts` has been removed.
+- The PHP function `gutenberg_revisions_link_to_editor` has been removed.
+- The PHP function `gutenberg_remember_classic_editor_when_saving_posts` has been removed.
+- The PHP function `gutenberg_can_edit_post_type` has been removed. Use [`use_block_editor_for_post_type`](https://developer.wordpress.org/reference/functions/use_block_editor_for_post_type/) instead.
+- The PHP function `gutenberg_can_edit_post` has been removed. Use [`use_block_editor_for_post`](https://developer.wordpress.org/reference/functions/use_block_editor_for_post/) instead.
+
+## 5.2.0
+
+- The PHP function `gutenberg_parse_blocks` has been removed. Use [`parse_blocks`](https://developer.wordpress.org/reference/functions/parse_blocks/) instead.
+- The PHP function `get_dynamic_blocks_regex` has been removed.
+- The PHP function `gutenberg_render_block` has been removed. Use [`render_block`](https://developer.wordpress.org/reference/functions/render_block/) instead.
+- The PHP function `strip_dynamic_blocks` has been removed. For use in excerpt preparation, consider [`excerpt_remove_blocks`](https://developer.wordpress.org/reference/functions/excerpt_remove_blocks/) instead.
+- The PHP function `strip_dynamic_blocks_add_filter` has been removed.
+- The PHP function `strip_dynamic_blocks_remove_filter` has been removed.
+- The PHP function `gutenberg_post_has_blocks` has been removed. Use [`has_blocks`](https://developer.wordpress.org/reference/functions/has_blocks/) instead.
+- The PHP function `gutenberg_content_has_blocks` has been removed. Use [`has_blocks`](https://developer.wordpress.org/reference/functions/has_blocks/) instead.
+- The PHP function `gutenberg_register_rest_routes` has been removed.
+- The PHP function `gutenberg_add_taxonomy_visibility_field` has been removed.
+- The PHP function `gutenberg_get_taxonomy_visibility_data` has been removed.
+- The PHP function `gutenberg_add_permalink_template_to_posts` has been removed.
+- The PHP function `gutenberg_add_block_format_to_post_content` has been removed.
+- The PHP function `gutenberg_add_target_schema_to_links` has been removed.
+- The PHP function `gutenberg_register_post_prepare_functions` has been removed.
+- The PHP function `gutenberg_silence_rest_errors` has been removed.
+- The PHP function `gutenberg_filter_post_type_labels` has been removed.
+- The PHP function `gutenberg_preload_api_request` has been removed. Use [`rest_preload_api_request`](https://developer.wordpress.org/reference/functions/rest_preload_api_request/) instead.
+- The PHP function `gutenberg_remove_wpcom_markdown_support` has been removed.
+- The PHP function `gutenberg_add_gutenberg_post_state` has been removed.
+- The PHP function `gutenberg_bulk_post_updated_messages` has been removed.
+- The PHP function `gutenberg_kses_allowedtags` has been removed.
+- The PHP function `gutenberg_add_responsive_body_class` has been removed.
+- The PHP function `gutenberg_add_edit_link_filters` has been removed.
+- The PHP function `gutenberg_add_edit_link` has been removed.
+- The PHP function `gutenberg_block_bulk_actions` has been removed.
+- The PHP function `gutenberg_replace_default_add_new_button` has been removed.
+- The PHP function `gutenberg_content_block_version` has been removed. Use [`block_version`](https://developer.wordpress.org/reference/functions/block_version/) instead.
+- The PHP function `gutenberg_get_block_categories` has been removed. Use [`get_block_categories`](https://developer.wordpress.org/reference/functions/get_block_categories/) instead.
+- The PHP function `register_tinymce_scripts` has been removed. Use [`wp_register_tinymce_scripts`](https://developer.wordpress.org/reference/functions/wp_register_tinymce_scripts/) instead.
+- The PHP function `gutenberg_register_post_types` has been removed.
+- The `gutenberg` theme support option has been removed. Use [`align-wide`](https://developer.wordpress.org/block-editor/developers/themes/theme-support/#wide-alignment) instead.
+- The PHP function `gutenberg_prepare_blocks_for_js` has been removed. Use [`get_block_editor_server_block_settings`](https://developer.wordpress.org/reference/functions/get_block_editor_server_block_settings/) instead.
+- The PHP function `gutenberg_load_list_reusable_blocks` has been removed.
+- The PHP function `_gutenberg_utf8_split` has been removed. Use `_mb_substr` instead.
+- The PHP function `gutenberg_disable_editor_settings_wpautop` has been removed.
+- The PHP function `gutenberg_add_rest_nonce_to_heartbeat_response_headers` has been removed.
+- The PHP function `gutenberg_check_if_classic_needs_warning_about_blocks` has been removed.
+- The PHP function `gutenberg_warn_classic_about_blocks` has been removed.
+- The PHP function `gutenberg_show_privacy_policy_help_text` has been removed.
+- The PHP function `gutenberg_common_scripts_and_styles` has been removed. Use [`wp_common_block_scripts_and_styles`](https://developer.wordpress.org/reference/functions/wp_common_block_scripts_and_styles/) instead.
+- The PHP function `gutenberg_enqueue_registered_block_scripts_and_styles` has been removed. Use [`wp_enqueue_registered_block_scripts_and_styles`](https://developer.wordpress.org/reference/functions/wp_enqueue_registered_block_scripts_and_styles/) instead.
+- The PHP function `gutenberg_meta_box_save` has been removed.
+- The PHP function `gutenberg_meta_box_save_redirect` has been removed.
+- The PHP function `gutenberg_filter_meta_boxes` has been removed.
+- The PHP function `gutenberg_intercept_meta_box_render` has been removed.
+- The PHP function `gutenberg_override_meta_box_callback` has been removed.
+- The PHP function `gutenberg_show_meta_box_warning` has been removed.
+- The PHP function `the_gutenberg_metaboxes` has been removed. Use [`the_block_editor_meta_boxes`](https://developer.wordpress.org/reference/functions/the_block_editor_meta_boxes/) instead.
+- The PHP function `gutenberg_meta_box_post_form_hidden_fields` has been removed. Use [`the_block_editor_meta_box_post_form_hidden_fields`](https://developer.wordpress.org/reference/functions/the_block_editor_meta_box_post_form_hidden_fields/) instead.
+- The PHP function `gutenberg_toggle_custom_fields` has been removed.
+- The PHP function `gutenberg_collect_meta_box_data` has been removed. Use [`register_and_do_post_meta_boxes`](https://developer.wordpress.org/reference/functions/register_and_do_post_meta_boxes/) instead.
+- `window._wpLoadGutenbergEditor` has been removed. Use `window._wpLoadBlockEditor` instead. Note: This is a private API, not intended for public use. It may be removed in the future.
+- The PHP function `gutenberg_get_script_polyfill` has been removed. Use [`wp_get_script_polyfill`](https://developer.wordpress.org/reference/functions/wp_get_script_polyfill/) instead.
+- The PHP function `gutenberg_add_admin_body_class` has been removed. Use the `.block-editor-page` class selector in your stylesheets if you need to scope styles to the block editor screen.
+
+## 4.5.0
+- `Dropdown.refresh()` has been deprecated as the contained `Popover` is now automatically refreshed.
+- `wp.editor.PostPublishPanelToggle` has been deprecated in favor of `wp.editor.PostPublishButton`.
+
+## 4.4.0
+
+- `wp.date.getSettings` has been removed. Please use `wp.date.__experimentalGetSettings` instead.
+- `wp.compose.remountOnPropChange` has been removed.
+- The following editor store actions have been removed: `createNotice`, `removeNotice`, `createSuccessNotice`, `createInfoNotice`, `createErrorNotice`, `createWarningNotice`. Use the equivalent actions by the same name from the `@wordpress/notices` module.
+- The id prop of wp.nux.DotTip has been removed. Please use the tipId prop instead.
+- `wp.blocks.isValidBlock` has been removed. Please use `wp.blocks.isValidBlockContent` instead but keep in mind that the order of params has changed.
+- `wp.data` `registry.registerReducer` has been deprecated. Use `registry.registerStore` instead.
+- `wp.data` `registry.registerSelectors` has been deprecated. Use `registry.registerStore` instead.
+- `wp.data` `registry.registerActions` has been deprecated. Use `registry.registerStore` instead.
+- `wp.data` `registry.registerResolvers` has been deprecated. Use `registry.registerStore` instead.
+- `moment` has been removed from the public API for the date module.
+
+## 4.3.0
+
+- `isEditorSidebarPanelOpened` selector (`core/edit-post`) has been removed. Please use `isEditorPanelEnabled` instead.
+- `toggleGeneralSidebarEditorPanel` action (`core/edit-post`) has been removed. Please use `toggleEditorPanelOpened` instead.
+- `wp.components.PanelColor` component has been removed. Please use `wp.editor.PanelColorSettings` instead.
+- `wp.editor.PanelColor` component has been removed. Please use `wp.editor.PanelColorSettings` instead.
+
+## 4.2.0
+
+- Writing resolvers as async generators has been removed. Use the controls plugin instead.
+- `wp.components.AccessibleSVG` component has been removed. Please use `wp.components.SVG` instead.
+- The `wp.editor.UnsavedChangesWarning` component no longer accepts a `forceIsDirty` prop.
+- `setActiveMetaBoxLocations` action (`core/edit-post`) has been removed.
+- `initializeMetaBoxState` action (`core/edit-post`) has been removed.
+- `wp.editPost.initializeEditor` no longer returns an object. Use the `setActiveMetaBoxLocations` action (`core/edit-post`) in place of the existing object's `initializeMetaBoxes` function.
+- `setMetaBoxSavedData` action (`core/edit-post`) has been removed.
+- `getMetaBoxes` selector (`core/edit-post`) has been removed. Use `getActiveMetaBoxLocations` selector (`core/edit-post`) instead.
+- `getMetaBox` selector (`core/edit-post`) has been removed. Use `isMetaBoxLocationActive` selector (`core/edit-post`) instead.
+- Attribute type coercion has been removed. Omit the source to preserve type via serialized comment demarcation.
+- `mediaDetails` in object passed to `onFileChange` callback of `wp.editor.mediaUpload`. Please use `media_details` property instead.
+- `wp.components.CodeEditor` has been removed. Used `wp.codeEditor` directly instead.
+- `wp.blocks.setUnknownTypeHandlerName` has been removed. Please use `setFreeformContentHandlerName` and `setUnregisteredTypeHandlerName` instead.
+- `wp.blocks.getUnknownTypeHandlerName` has been removed. Please use `getFreeformContentHandlerName` and `getUnregisteredTypeHandlerName` instead.
+- The Reusable Blocks Data API was marked as experimental as it's subject to change in the future.
+
+## 4.1.0
+
+- `wp.data.dispatch( 'core/editor' ).checkTemplateValidity` has been removed. Validity is verified automatically upon block reset.
+
+## 4.0.0
+
+- `wp.editor.RichTextProvider` has been removed. Please use `wp.data.select( 'core/editor' )` methods instead.
+- `wp.components.Draggable` as a DOM node drag handler has been removed. Please, use `wp.components.Draggable` as a wrap component for your DOM node drag handler.
+- `wp.i18n.getI18n` has been removed. Use `__`, `_x`, `_n`, or `_nx` instead.
+- `wp.i18n.dcnpgettext` has been removed. Use `__`, `_x`, `_n`, or `_nx` instead.
+
+## 3.9.0
+
+- RichText `getSettings` prop has been removed. The `unstableGetSettings` prop is available if continued use is required. Unstable APIs are strongly discouraged to be used, and are subject to removal without notice.
+- RichText `onSetup` prop has been removed. The `unstableOnSetup` prop is available if continued use is required. Unstable APIs are strongly discouraged to be used, and are subject to removal without notice.
+- `wp.editor.getColorName` has been removed. Please use `wp.editor.getColorObjectByColorValue` instead.
+- `wp.editor.getColorClass` has been renamed. Please use `wp.editor.getColorClassName` instead.
+- `value` property in color objects passed by `wp.editor.withColors` has been removed. Please use color property instead.
+- The Subheading block has been removed. Please use the Paragraph block instead.
+- `wp.blocks.getDefaultBlockForPostFormat` has been removed.
+
+## 3.8.0
+
+ - `wp.components.withContext` has been removed. Please use `wp.element.createContext` instead. See: https://reactjs.org/docs/context.html.
+ - `wp.coreBlocks.registerCoreBlocks` has been removed. Please use `wp.blockLibrary.registerCoreBlocks` instead.
+ - `wp.editor.DocumentTitle` component has been removed.
+ - `getDocumentTitle` selector (`core/editor`) has been removed.
+
+## 3.7.0
+
+ - `wp.components.withAPIData` has been removed. Please use the Core Data module or `wp.apiFetch` directly instead.
+ - `wp.data.dispatch("core").receiveTerms` has been deprecated. Please use `wp.data.dispatch("core").receiveEntityRecords` instead.
+ - `getCategories` resolver has been deprecated. Please use `getEntityRecords` resolver instead.
+ - `wp.data.select("core").getTerms` has been deprecated. Please use `wp.data.select("core").getEntityRecords` instead.
+ - `wp.data.select("core").getCategories` has been deprecated. Please use `wp.data.select("core").getEntityRecords` instead.
+ - `wp.data.select("core").isRequestingCategories` has been deprecated. Please use `wp.data.select("core/data").isResolving` instead.
+ - `wp.data.select("core").isRequestingTerms` has been deprecated. Please use `wp.data.select("core").isResolving` instead.
+ - `wp.data.restrictPersistence`, `wp.data.setPersistenceStorage` and `wp.data.setupPersistence` has been removed. Please use the data persistence plugin instead.
+
+## 3.6.0
+
+ - `wp.editor.editorMediaUpload` has been removed. Please use `wp.editor.mediaUpload` instead.
+ - `wp.utils.getMimeTypesArray` has been removed.
+ - `wp.utils.mediaUpload` has been removed. Please use `wp.editor.mediaUpload` instead.
+ - `wp.utils.preloadImage` has been removed.
+ - `supports.wideAlign` has been removed from the Block API. Please use `supports.alignWide` instead.
+ - `wp.blocks.isSharedBlock` has been removed. Use `wp.blocks.isReusableBlock` instead.
+ - `fetchSharedBlocks` action (`core/editor`) has been removed. Use `fetchReusableBlocks` instead.
+ - `receiveSharedBlocks` action (`core/editor`) has been removed. Use `receiveReusableBlocks` instead.
+ - `saveSharedBlock` action (`core/editor`) has been removed. Use `saveReusableBlock` instead.
+ - `deleteSharedBlock` action (`core/editor`) has been removed. Use `deleteReusableBlock` instead.
+ - `updateSharedBlockTitle` action (`core/editor`) has been removed. Use `updateReusableBlockTitle` instead.
+ - `convertBlockToSaved` action (`core/editor`) has been removed. Use `convertBlockToReusable` instead.
+ - `getSharedBlock` selector (`core/editor`) has been removed. Use `getReusableBlock` instead.
+ - `isSavingSharedBlock` selector (`core/editor`) has been removed. Use `isSavingReusableBlock` instead.
+ - `isFetchingSharedBlock` selector (`core/editor`) has been removed. Use `isFetchingReusableBlock` instead.
+ - `getSharedBlocks` selector (`core/editor`) has been removed. Use `getReusableBlocks` instead.
+
+## 3.5.0
+
+ - `wp.components.ifCondition` has been removed. Please use `wp.compose.ifCondition` instead.
+ - `wp.components.withGlobalEvents` has been removed. Please use `wp.compose.withGlobalEvents` instead.
+ - `wp.components.withInstanceId` has been removed. Please use `wp.compose.withInstanceId` instead.
+ - `wp.components.withSafeTimeout` has been removed. Please use `wp.compose.withSafeTimeout` instead.
+ - `wp.components.withState` has been removed. Please use `wp.compose.withState` instead.
+ - `wp.element.pure` has been removed. Please use `wp.compose.pure` instead.
+ - `wp.element.compose` has been removed. Please use `wp.compose.compose` instead.
+ - `wp.element.createHigherOrderComponent` has been removed. Please use `wp.compose.createHigherOrderComponent` instead.
+ - `wp.utils.buildTermsTree` has been removed.
+ - `wp.utils.decodeEntities` has been removed. Please use `wp.htmlEntities.decodeEntities` instead.
+ - All references to a block's `uid` have been replaced with equivalent props and selectors for `clientId`.
+ - The `wp.editor.MediaPlaceholder` component `onSelectUrl` prop has been renamed to `onSelectURL`.
+ - The `wp.editor.UrlInput` component has been renamed to `wp.editor.URLInput`.
+ - The Text Columns block has been removed. Please use the Columns block instead.
+ - `InnerBlocks` grouped layout is removed. Use intermediary nested inner blocks instead. See Columns / Column block for reference implementation.
+ - `RichText` explicit `element` format removed. Please use the compatible `children` format instead.
+
+## 3.4.0
+
+ - `focusOnMount` prop in the `Popover` component has been changed from `Boolean`-only to an enum-style property that accepts `"firstElement"`, `"container"`, or `false`. Please convert any `` usage to ``.
+ - `wp.utils.keycodes` utilities are removed. Please use `wp.keycodes` instead.
+ - Block `id` prop in `edit` function removed. Please use block `clientId` prop instead.
+ - `property` source removed. Please use equivalent `text`, `html`, or `attribute` source, or comment attribute instead.
+
+## 3.3.0
+
+ - `useOnce: true` has been removed from the Block API. Please use `supports.multiple: false` instead.
+ - Serializing components using `componentWillMount` lifecycle method. Please use the constructor instead.
+ - `blocks.Autocomplete.completers` filter removed. Please use `editor.Autocomplete.completers` instead.
+ - `blocks.BlockEdit` filter removed. Please use `editor.BlockEdit` instead.
+ - `blocks.BlockListBlock` filter removed. Please use `editor.BlockListBlock` instead.
+ - `blocks.MediaUpload` filter removed. Please use `editor.MediaUpload` instead.
+
+## 3.2.0
+
+ - `wp.data.withRehydratation` has been renamed to `wp.data.withRehydration`.
+ - The `wp.editor.ImagePlaceholder` component is removed. Please use `wp.editor.MediaPlaceholder` instead.
+ - `wp.utils.deprecated` function removed. Please use `wp.deprecated` instead.
+ - `wp.utils.blob` removed. Please use `wp.blob` instead.
+ - `getInserterItems`: the `allowedBlockTypes` argument was removed and the `parentUID` argument was added.
+ - `getFrecentInserterItems` selector removed. Please use `getInserterItems` instead.
+ - `getSupportedBlocks` selector removed. Please use `canInsertBlockType` instead.
+
+## 3.1.0
+
+ - All components in `wp.blocks.*` are removed. Please use `wp.editor.*` instead.
+ - `wp.blocks.withEditorSettings` is removed. Please use the data module to access the editor settings `wp.data.select( "core/editor" ).getEditorSettings()`.
+ - All DOM utils in `wp.utils.*` are removed. Please use `wp.dom.*` instead.
+ - `isPrivate: true` has been removed from the Block API. Please use `supports.inserter: false` instead.
+ - `wp.utils.isExtraSmall` function removed. Please use `wp.viewport` module instead.
+ - `getEditedPostExcerpt` selector removed (`core/editor`). Use `getEditedPostAttribute( 'excerpt' )` instead.
+
+## 3.0.0
+
+ - `wp.blocks.registerCoreBlocks` function removed. Please use `wp.coreBlocks.registerCoreBlocks` instead.
+ - Raw TinyMCE event handlers for `RichText` have been deprecated. Please use [documented props](/packages/editor/src/components/rich-text/README.md), ancestor event handler, or onSetup access to the internal editor instance event hub instead.
+
+## 2.8.0
+
+ - `Original autocompleter interface in wp.components.Autocomplete` updated. Please use `latest autocompleter interface` instead. See [autocomplete](/packages/components/src/autocomplete/README.md) for more info.
+ - `getInserterItems`: the `allowedBlockTypes` argument is now mandatory.
+ - `getFrecentInserterItems`: the `allowedBlockTypes` argument is now mandatory.
+
+## 2.7.0
+
+ - `wp.element.getWrapperDisplayName` function removed. Please use `wp.element.createHigherOrderComponent` instead.
+
+## 2.6.0
+
+ - `wp.blocks.getBlockDefaultClassname` function removed. Please use `wp.blocks.getBlockDefaultClassName` instead.
+ - `wp.blocks.Editable` component removed. Please use the `wp.blocks.RichText` component instead.
+
+## 2.5.0
+
+ - Returning raw HTML from block `save` is unsupported. Please use the `wp.element.RawHTML` component instead.
+ - `wp.data.query` higher-order component removed. Please use `wp.data.withSelect` instead.
+
+## 2.4.0
+
+ - `wp.blocks.BlockDescription` component removed. Please use the `description` block property instead.
+ - `wp.blocks.InspectorControls.*` components removed. Please use `wp.components.*` components instead.
+ - `wp.blocks.source.*` matchers removed. Please use the declarative attributes instead. See [block attributes](/docs/designers-developers/developers/block-api/block-attributes.md) for more info.
+ - `wp.data.select( 'selector', ...args )` removed. Please use `wp.data.select( reducerKey' ).*` instead.
+ - `wp.blocks.MediaUploadButton` component removed. Please use `wp.blocks.MediaUpload` component instead.
diff --git a/gb-src/docs/designers-developers/developers/backward-compatibility/meta-box.md b/gb-src/docs/designers-developers/developers/backward-compatibility/meta-box.md
new file mode 100644
index 0000000000000..dc8ce593eb856
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/backward-compatibility/meta-box.md
@@ -0,0 +1,80 @@
+# Meta Boxes
+
+This is a brief document detailing how meta box support works in the block editor. With the superior developer and user experience of blocks, especially once block templates are available, **porting PHP meta boxes to blocks is highly encouraged!** See the [Meta Block tutorial](/docs/designers-developers/developers/tutorials/metabox/meta-block-1-intro.md) for how to store post meta data using blocks.
+
+### Testing, Converting, and Maintaining Existing Meta Boxes
+
+Before converting meta boxes to blocks, it may be easier to test if a meta box works with the block editor, and explicitly mark it as such.
+
+If a meta box *doesn't* work with the block editor, and updating it to work correctly is not an option, the next step is to add the `__block_editor_compatible_meta_box` argument to the meta box declaration:
+
+```php
+add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
+ null, 'normal', 'high',
+ array(
+ '__block_editor_compatible_meta_box' => false,
+ )
+);
+```
+
+WordPress won't show the meta box but a message saying that it isn't compatible with the block editor, including a link to the Classic Editor plugin. By default, `__block_editor_compatible_meta_box` is `true`.
+
+After a meta box is converted to a block, it can be declared as existing for backward compatibility:
+
+```php
+add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
+ null, 'normal', 'high',
+ array(
+ '__back_compat_meta_box' => true,
+ )
+);
+```
+
+When the block editor is used, this meta box will no longer be displayed in the meta box area, as it now only exists for backward compatibility purposes. It will continue to display correctly in the classic editor.
+
+### Meta Box Data Collection
+
+On each block editor page load, we register an action that collects the meta box data to determine if an area is empty. The original global state is reset upon collection of meta box data.
+
+See [`register_and_do_post_meta_boxes`](https://developer.wordpress.org/reference/functions/register_and_do_post_meta_boxes/).
+
+It will run through the functions and hooks that `post.php` runs to register meta boxes; namely `add_meta_boxes`, `add_meta_boxes_{$post->post_type}`, and `do_meta_boxes`.
+
+Meta boxes are filtered to strip out any core meta boxes, standard custom taxonomy meta boxes, and any meta boxes that have declared themselves as only existing for backward compatibility purposes.
+
+Then each location for this particular type of meta box is checked for whether it is active. If it is not empty a value of true is stored, if it is empty a value of false is stored. This meta box location data is then dispatched by the editor Redux store in `INITIALIZE_META_BOX_STATE`.
+
+Ideally, this could be done at instantiation of the editor and help simplify this flow. However, it is not possible to know the meta box state before `admin_enqueue_scripts`, where we are calling `initializeEditor()`. This will have to do, unless we want to move `initializeEditor()` to fire in the footer or at some point after `admin_head`. With recent changes to editor bootstrapping this might now be possible. Test with ACF to make sure.
+
+### Redux and React Meta Box Management
+
+When rendering the block editor, the meta boxes are rendered to a hidden div `#metaboxes`.
+
+*The Redux store will hold all meta boxes as inactive by default*. When
+`INITIALIZE_META_BOX_STATE` comes in, the store will update any active meta box areas by setting the `isActive` flag to `true`. Once this happens React will check for the new props sent in by Redux on the `MetaBox` component. If that `MetaBox` is now active, instead of rendering null, a `MetaBoxArea` component will be rendered. The `MetaBox` component is the container component that mediates between the `MetaBoxArea` and the Redux Store. *If no meta boxes are active, nothing happens. This will be the default behavior, as all core meta boxes have been stripped.*
+
+#### MetaBoxArea Component
+
+When the component renders it will store a reference to the meta boxes container and retrieve the meta boxes HTML from the prefetch location.
+
+When the post is updated, only meta box areas that are active will be submitted. This prevents unnecessary requests. No extra revisions are created by the meta box submissions. A Redux action will trigger on `REQUEST_POST_UPDATE` for any active meta box. See `editor/effects.js`. The `REQUEST_META_BOX_UPDATES` action will set that meta box's state to `isUpdating`. The `isUpdating` prop will be sent into the `MetaBoxArea` and cause a form submission.
+
+When the meta box area is saving, we display an updating overlay, to prevent users from changing the form values while a save is in progress.
+
+An example save url would look like:
+
+`mysite.com/wp-admin/post.php?post=1&action=edit&meta-box-loader=1`
+
+This url is automatically passed into React via a `_wpMetaBoxUrl` global variable.
+
+This page mimics the `post.php` post form, so when it is submitted it will fire all of the normal hooks and actions, and have the proper global state to correctly fire any PHP meta box mumbo jumbo without needing to modify any existing code. On successful submission, React will signal a `handleMetaBoxReload` to remove the updating overlay.
+
+### Common Compatibility Issues
+
+Most PHP meta boxes should continue to work in the block editor, but some meta boxes that include advanced functionality could break. Here are some common reasons why meta boxes might not work as expected in the block editor:
+
+- Plugins relying on selectors that target the post title, post content fields, and other metaboxes (of the old editor).
+- Plugins relying on TinyMCE's API because there's no longer a single TinyMCE instance to talk to in the block editor.
+- Plugins making updates to their DOM on "submit" or on "save".
+
+Please also note that if your plugin triggers a PHP warning or notice to be output on the page, this will cause the HTML document type (``) to be output incorrectly. This will cause the browser to render using "Quirks Mode", which is a compatibility layer that gets enabled when the browser doesn't know what type of document it is parsing. The block editor is not meant to work in this mode, but it can _appear_ to be working just fine. If you encounter issues such as *meta boxes overlaying the editor* or other layout issues, please check the raw page source of your document to see that the document type definition is the first thing output on the page. There will also be a warning in the JavaScript console, noting the issue.
diff --git a/gb-src/docs/designers-developers/developers/block-api/README.md b/gb-src/docs/designers-developers/developers/block-api/README.md
new file mode 100644
index 0000000000000..56d159bda6d13
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/README.md
@@ -0,0 +1,11 @@
+# Block API Reference
+
+Blocks are the fundamental element of the editor. They are the primary way in which plugins and themes can register their own functionality and extend the capabilities of the editor.
+
+## Registering a block
+
+All blocks must be registered before they can be used in the editor. You can learn about block registration, and the available options, in the [block registration](/docs/designers-developers/developers/block-api/block-registration.md) documentation.
+
+## Block `edit` and `save`
+
+The `edit` and `save` functions define the editor interface with which a user would interact, and the markup to be serialized back when a post is saved. They are the heart of how a block operates, so they are [covered separately](/docs/designers-developers/developers/block-api/block-edit-save.md).
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-annotations.md b/gb-src/docs/designers-developers/developers/block-api/block-annotations.md
new file mode 100644
index 0000000000000..d74a300c54f0b
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-annotations.md
@@ -0,0 +1,59 @@
+# Annotations
+
+**Note: This API is experimental, that means it is subject to non-backward compatible changes or removal in any future version.**
+
+Annotations are a way to highlight a specific piece in a post created with the block editor. Examples of this include commenting on a piece of text and spellchecking. Both can use the annotations API to mark a piece of text.
+
+## API
+
+To see the API for yourself the easiest way is to have a block that is at least 200 characters long without formatting and putting the following in the console:
+
+```js
+wp.data.dispatch( 'core/annotations' ).addAnnotation( {
+ source: "my-annotations-plugin",
+ blockClientId: wp.data.select( 'core/editor' ).getBlockOrder()[0],
+ richTextIdentifier: "content",
+ range: {
+ start: 50,
+ end: 100,
+ },
+} );
+```
+
+The start and the end of the range should be calculated based only on the text of the relevant `RichText`. For example, in the following HTML position 0 will refer to the position before the capital S:
+
+```html
+Strong text
+```
+
+To help with determining the correct positions, the `wp.richText.create` method can be used. This will split a piece of HTML into text and formats.
+
+All available properties can be found in the API documentation of the `addAnnotation` action.
+
+The property `richTextIdentifier` is the identifier of the RichText instance the annotation applies to. This is necessary because blocks may have multiple rich text instances that are used to manage data for different attributes, so you need to pass this in order to highlight text within the correct one.
+
+For example the Paragraph block only has a single RichText instance, with the identifer `content`. The quote block type has 2 RichText instances, so if you wish to highlight text in the citation, you need to pass `citation` as the `richTextIdentifier` when adding an annotation. To target the quote content, you need to use the identifier `value`. Refer to the source code of the block type to find the correct identifier.
+
+## Block annotation
+
+It is also possible to annotate a block completely. In that case just provide the `selector` property and set it to `block`. The default `selector` is `range`, which can be used for text annotation.
+
+```js
+wp.data.dispatch( 'core/annotations' ).addAnnotation( {
+ source: "my-annotations-plugin",
+ blockClientId: wp.data.select( 'core/editor' ).getBlockOrder()[0],
+ selector: "block",
+} );
+```
+
+This doesn't provide any styling out of the box, so you have to provide some CSS to make sure your annotation is shown:
+
+```css
+.is-annotated-by-my-annotations-plugin {
+ outline: 1px solid black;
+}
+```
+
+## Text annotation
+
+The text annotation is controlled by the `start` and `end` properties. Simple `start` and `end` properties don't work for HTML, so these properties are assumed to be offsets within the `rich-text` internal structure. For simplicity you can think about this as if all HTML would be stripped out and then you calculate the `start` and the `end` of the annotation.
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-attributes.md b/gb-src/docs/designers-developers/developers/block-api/block-attributes.md
new file mode 100644
index 0000000000000..65386bba141c0
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-attributes.md
@@ -0,0 +1,222 @@
+# Attributes
+
+## Common Sources
+
+Attribute sources are used to define how the block attribute values are extracted from saved post content. They provide a mechanism to map from the saved markup to a JavaScript representation of a block.
+
+If no attribute source is specified, the attribute will be saved to (and read from) the block's [comment delimiter](/docs/designers-developers/key-concepts.md#delimiters-and-parsing-expression-grammar).
+
+The keys specified in the attributes source object are named as you see fit. The result of the attribute source definition is assigned as a value to each key.
+
+If no selector argument is specified, the source definition runs against the block's root node. If a selector argument is specified, it will run against the specified element(s) contained within the block.
+
+The selector specified can be an HTML tag, or anything queryable such as a class or id attribute, see examples below.
+
+Under the hood, attribute sources are a superset of the functionality provided by [hpq](https://github.com/aduth/hpq), a small library used to parse and query HTML markup into an object shape.
+
+
+### `attribute`
+
+Use `attribute` to extract the value of an attribute from markup.
+
+_Example_: Extract the `src` attribute from an image found in the block's markup.
+
+```js
+{
+ url: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'src',
+ }
+}
+// { "url": "https://lorempixel.com/1200/800/" }
+```
+
+#### `attribute` Type Validation
+
+Accepted values in the `type` field of an `attribute` MUST be one of the following:
+
+* null
+* boolean
+* object
+* array
+* number
+* string
+* integer
+
+See [WordPress's REST API documentation](https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/) for additional details.
+
+### `text`
+
+Use `text` to extract the inner text from markup.
+
+```js
+{
+ content: {
+ type: 'string',
+ source: 'text',
+ selector: 'figcaption',
+ }
+}
+// { "content": "The inner text of the figcaption element" }
+```
+
+Another example, using `text` as the source, and using `.my-content` class as the selector to extract text:
+
+```js
+{
+ content: {
+ type: 'string',
+ source: 'text',
+ selector: '.my-content',
+ }
+}
+// { "content": "The inner text of .my-content class" }
+```
+
+### `html`
+
+Use `html` to extract the inner HTML from markup.
+
+```js
+{
+ content: {
+ type: 'string',
+ source: 'html',
+ selector: 'figcaption',
+ }
+}
+// { "content": "The inner text of the figcaption element" }
+```
+
+Use the `multiline` property to extract the inner HTML of matching tag names for the use in `RichText` with the `multiline` prop.
+
+```js
+{
+ content: {
+ type: 'string',
+ source: 'html',
+ multiline: 'p',
+ selector: 'blockquote',
+ }
+}
+// { "content": "
First line
Second line
" }
+```
+
+### `query`
+
+Use `query` to extract an array of values from markup. Entries of the array are determined by the selector argument, where each matched element within the block will have an entry structured corresponding to the second argument, an object of attribute sources.
+
+_Example_: Extract `src` and `alt` from each image element in the block's markup.
+
+```js
+{
+ images: {
+ type: 'array',
+ source: 'query'
+ selector: 'img',
+ query: {
+ url: {
+ type: 'string',
+ source: 'attribute',
+ attribute: 'src',
+ },
+ alt: {
+ type: 'string',
+ source: 'attribute',
+ attribute: 'alt',
+ },
+ }
+ }
+}
+// {
+// "images": [
+// { "url": "https://lorempixel.com/1200/800/", "alt": "large image" },
+// { "url": "https://lorempixel.com/50/50/", "alt": "small image" }
+// ]
+// }
+```
+
+## Meta
+
+Attributes may be obtained from a post's meta rather than from the block's representation in saved post content. For this, an attribute is required to specify its corresponding meta key under the `meta` key:
+
+```js
+attributes: {
+ author: {
+ type: 'string',
+ source: 'meta',
+ meta: 'author'
+ },
+},
+```
+
+From here, meta attributes can be read and written by a block using the same interface as any attribute:
+
+{% codetabs %}
+{% ES5 %}
+```js
+edit: function( props ) {
+ function onChange( event ) {
+ props.setAttributes( { author: event.target.value } );
+ }
+
+ return el( 'input', {
+ value: props.attributes.author,
+ onChange: onChange,
+ } );
+},
+```
+{% ESNext %}
+```js
+edit( { attributes, setAttributes } ) {
+ function onChange( event ) {
+ setAttributes( { author: event.target.value } );
+ }
+
+ return ;
+},
+```
+{% end %}
+
+### Considerations
+
+By default, a meta field will be excluded from a post object's meta. This can be circumvented by explicitly making the field visible:
+
+```php
+function gutenberg_my_block_init() {
+ register_post_meta( 'post', 'author', array(
+ 'show_in_rest' => true,
+ ) );
+}
+add_action( 'init', 'gutenberg_my_block_init' );
+```
+
+Furthermore, be aware that WordPress defaults to:
+
+- not treating a meta datum as being unique, instead returning an array of values;
+- treating that datum as a string.
+
+If either behavior is not desired, the same `register_post_meta` call can be complemented with the `single` and/or `type` parameters as follows:
+
+```php
+function gutenberg_my_block_init() {
+ register_post_meta( 'post', 'author_count', array(
+ 'show_in_rest' => true,
+ 'single' => true,
+ 'type' => 'integer',
+ ) );
+}
+add_action( 'init', 'gutenberg_my_block_init' );
+```
+
+If you'd like to use an object or an array in an attribute, you can register a `string` attribute type and use JSON as the intermediary. Serialize the structured data to JSON prior to saving, and then deserialize the JSON string on the server. Keep in mind that you're responsible for the integrity of the data; make sure to properly sanitize, accommodate missing data, etc.
+
+Lastly, make sure that you respect the data's type when setting attributes, as the framework does not automatically perform type casting of meta. Incorrect typing in block attributes will result in a post remaining dirty even after saving (_cf._ `isEditedPostDirty`, `hasEditedAttributes`). For instance, if `authorCount` is an integer, remember that event handlers may pass a different kind of data, thus the value should be cast explicitly:
+
+```js
+function onChange( event ) {
+ props.setAttributes( { authorCount: Number( event.target.value ) } );
+}
+```
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-deprecation.md b/gb-src/docs/designers-developers/developers/block-api/block-deprecation.md
new file mode 100644
index 0000000000000..4645b3b5207d6
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-deprecation.md
@@ -0,0 +1,280 @@
+# Deprecated Blocks
+
+When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. In order to provide a good upgrade path, you can choose one of the following strategies:
+
+ - Do not deprecate the block and create a new one (a different name)
+ - Provide a "deprecated" version of the block allowing users opening these in the block editor to edit them using the updated block.
+
+A block can have several deprecated versions. A deprecation will be tried if a parsed block appears to be invalid, or if there is a deprecation defined for which its `isEligible` property function returns true.
+
+Deprecations are defined on a block type as its `deprecated` property, an array of deprecation objects where each object takes the form:
+
+- `attributes` (Object): The [attributes definition](/docs/designers-developers/developers/block-api/block-attributes.md) of the deprecated form of the block.
+- `support` (Object): The [supports definition](/docs/designers-developers/developers/block-api/block-registration.md) of the deprecated form of the block.
+- `save` (Function): The [save implementation](/docs/designers-developers/developers/block-api/block-edit-save.md) of the deprecated form of the block.
+- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block.
+- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks.
+
+It's important to note that `attributes`, `support`, and `save` are not automatically inherited from the current version, since they can impact parsing and serialization of a block, so they must be defined on the deprecated object in order to be processed during a migration.
+
+### Example:
+
+{% codetabs %}
+{% ES5 %}
+```js
+var el = wp.element.createElement,
+ registerBlockType = wp.blocks.registerBlockType,
+ attributes = {
+ text: {
+ type: 'string',
+ default: 'some random value',
+ }
+ };
+
+registerBlockType( 'gutenberg/block-with-deprecated-version', {
+
+ // ... other block properties go here
+
+ attributes: attributes,
+
+ save: function( props ) {
+ return el( 'div', {}, props.attributes.text );
+ },
+
+ deprecated: [
+ {
+ attributes: attributes,
+
+ save: function( props ) {
+ return el( 'p', {}, props.attributes.text );
+ },
+ }
+ ]
+} );
+```
+{% ESNext %}
+```js
+const { registerBlockType } = wp.blocks;
+const attributes = {
+ text: {
+ type: 'string',
+ default: 'some random value',
+ }
+};
+
+registerBlockType( 'gutenberg/block-with-deprecated-version', {
+
+ // ... other block properties go here
+
+ attributes,
+
+ save( props ) {
+ return
;
+ },
+ }
+ ]
+} );
+```
+{% end %}
+
+In the example above we updated the block to use an inner Paragraph block with a title instead of a title attribute.
+
+*Above are example cases of block deprecation. For more, real-world examples, check for deprecations in the [core block library](/packages/block-library/src/README.md). Core blocks have been updated across releases and contain simple and complex deprecations.*
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-edit-save.md b/gb-src/docs/designers-developers/developers/block-api/block-edit-save.md
new file mode 100644
index 0000000000000..5d0cc72b6c6b7
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-edit-save.md
@@ -0,0 +1,418 @@
+# Edit and Save
+
+When registering a block, the `edit` and `save` functions provide the interface for how a block is going to be rendered within the editor, how it will operate and be manipulated, and how it will be saved.
+
+## Edit
+
+The `edit` function describes the structure of your block in the context of the editor. This represents what the editor will render when the block is used.
+
+{% codetabs %}
+{% ES5 %}
+```js
+// A static div
+edit: function() {
+ return wp.element.createElement(
+ 'div',
+ null,
+ 'Your block.'
+ );
+}
+```
+{% ESNext %}
+```jsx
+edit: () => {
+ return
Your block.
;
+}
+```
+{% end %}
+
+The function receives the following properties through an object argument:
+
+### attributes
+
+This property surfaces all the available attributes and their corresponding values, as described by the `attributes` property when the block type was registered. See [attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md) for how to specify attribute sources.
+
+In this case, assuming we had defined an attribute of `content` during block registration, we would receive and use that value in our edit function:
+
+{% codetabs %}
+{% ES5 %}
+```js
+edit: function( props ) {
+ return wp.element.createElement(
+ 'div',
+ null,
+ props.attributes.content
+ );
+}
+```
+{% ESNext %}
+```js
+edit: ( { attributes } ) => {
+ return
{ attributes.content }
;
+}
+```
+{% end %}
+
+The value of `attributes.content` will be displayed inside the `div` when inserting the block in the editor.
+
+### className
+
+This property returns the class name for the wrapper element. This is automatically added in the `save` method, but not on `edit`, as the root element may not correspond to what is _visually_ the main element of the block. You can request it to add it to the correct element in your function.
+
+{% codetabs %}
+{% ES5 %}
+```js
+edit: function( props ) {
+ return wp.element.createElement(
+ 'div',
+ { className: props.className },
+ props.attributes.content
+ );
+}
+```
+{% ESNext %}
+```js
+edit: ( { attributes, className } ) => {
+ return
{ attributes.content }
;
+}
+```
+{% end %}
+
+### isSelected
+
+The isSelected property is an object that communicates whether the block is currently selected.
+
+{% codetabs %}
+{% ES5 %}
+```js
+edit: function( props ) {
+ return wp.element.createElement(
+ 'div',
+ { className: props.className },
+ [
+ 'Your block.',
+ props.isSelected ? wp.element.createElement(
+ 'span',
+ null,
+ 'Shows only when the block is selected.'
+ )
+ ]
+ );
+}
+```
+{% ESNext %}
+```jsx
+edit: ( { attributes, className, isSelected } ) => {
+ return (
+
+ Your block.
+ { isSelected &&
+ Shows only when the block is selected.
+ }
+
+ );
+}
+```
+{% end %}
+
+### setAttributes
+
+This function allows the block to update individual attributes based on user interactions.
+
+{% codetabs %}
+{% ES5 %}
+```js
+edit: function( props ) {
+ // Simplify access to attributes
+ let content = props.attributes.content;
+ let mySetting = props.attributes.mySetting;
+
+ // Toggle a setting when the user clicks the button
+ let toggleSetting = () => props.setAttributes( { mySetting: ! mySetting } );
+ return wp.element.createElement(
+ 'div',
+ { className: props.className },
+ [
+ content,
+ props.isSelected ? wp.element.createElement(
+ 'button',
+ { onClick: toggleSetting },
+ 'Toggle setting'
+ ) : null
+ ]
+ );
+},
+```
+{% ESNext %}
+```jsx
+edit: ( { attributes, setAttributes, className, isSelected } ) => {
+ // Simplify access to attributes
+ const { content, mySetting } = attributes;
+
+ // Toggle a setting when the user clicks the button
+ const toggleSetting = () => setAttributes( { mySetting: ! mySetting } );
+ return (
+
+ { content }
+ { isSelected &&
+
+ }
+
+ );
+}
+```
+{% end %}
+
+When using attributes that are objects or arrays it's a good idea to copy or clone the attribute prior to updating it:
+
+{% codetabs %}
+{% ES5 %}
+```js
+// Good - cloning the old list
+var newList = attributes.list.slice();
+
+var addListItem = function( newListItem ) {
+ setAttributes( { list: newList.concat( [ newListItem ] ) } );
+};
+
+// Bad - the list from the existing attribute is modified directly to add the new list item:
+var list = attributes.list;
+var addListItem = function( newListItem ) {
+ list.push( newListItem );
+ setAttributes( { list: list } );
+};
+```
+{% ESNext %}
+```js
+// Good - a new array is created from the old list attribute and a new list item:
+const { list } = attributes;
+const addListItem = ( newListItem ) => setAttributes( { list: [ ...list, newListItem ] } );
+
+// Bad - the list from the existing attribute is modified directly to add the new list item:
+const { list } = attributes;
+const addListItem = ( newListItem ) => {
+ list.push( newListItem );
+ setAttributes( { list } );
+};
+```
+{% end %}
+
+Why do this? In JavaScript, arrays and objects are passed by reference, so this practice ensures changes won't affect other code that might hold references to the same data. Furthermore, the Gutenberg project follows the philosophy of the Redux library that [state should be immutable](https://redux.js.org/faq/immutable-data#what-are-the-benefits-of-immutability)—data should not be changed directly, but instead a new version of the data created containing the changes.
+
+## Save
+
+The `save` function defines the way in which the different attributes should be combined into the final markup, which is then serialized into `post_content`.
+
+{% codetabs %}
+{% ES5 %}
+```js
+save: function() {
+ return wp.element.createElement(
+ 'div',
+ null,
+ 'Your block.'
+ );
+}
+```
+{% ESNext %}
+```jsx
+save: () => {
+ return
Your block.
;
+}
+```
+{% end %}
+
+For most blocks, the return value of `save` should be an [instance of WordPress Element](/packages/element/README.md) representing how the block is to appear on the front of the site.
+
+_Note:_ While it is possible to return a string value from `save`, it _will be escaped_. If the string includes HTML markup, the markup will be shown on the front of the site verbatim, not as the equivalent HTML node content. If you must return raw HTML from `save`, use `wp.element.RawHTML`. As the name implies, this is prone to [cross-site scripting](https://en.wikipedia.org/wiki/Cross-site_scripting) and therefore is discouraged in favor of a WordPress Element hierarchy whenever possible.
+
+_Note:_ The save function should be a pure function that depends only on the attributes used to invoke it.
+It can not have any side effect or retrieve information from another source, e.g. it is not possible to use the data module inside it `select( store ).selector( ... )`.
+This is because if the external information changes, the block may be flagged as invalid when the post is later edited ([read more about Validation](#validation)).
+If there is a need to have other information as part of the save, developers can consider one of these two alternatives:
+ - Use [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md) and dynamically retrieve the required information on the server.
+ - Store the external value as an attribute which is dynamically updated in the block's `edit` function as changes occur.
+
+For [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md), the return value of `save` could represent a cached copy of the block's content to be shown only in case the plugin implementing the block is ever disabled.
+
+If left unspecified, the default implementation will save no markup in post content for the dynamic block, instead deferring this to always be calculated when the block is shown on the front of the site.
+
+### attributes
+
+As with `edit`, the `save` function also receives an object argument including attributes which can be inserted into the markup.
+
+{% codetabs %}
+{% ES5 %}
+```js
+save: function( props ) {
+ return wp.element.createElement(
+ 'div',
+ null,
+ props.attributes.content
+ );
+}
+```
+{% ESNext %}
+```jsx
+save: ( { attributes } ) => {
+ return
{ attributes.content }
;
+}
+```
+{% end %}
+
+
+When saving your block, you want to save the attributes in the same format specified by the attribute source definition. If no attribute source is specified, the attribute will be saved to the block's comment delimiter. See the [Block Attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md) for more details.
+
+## Examples
+
+Here are a couple examples of using attributes, edit, and save all together. For a full working example, see the [Introducing Attributes and Editable Fields](/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md) section of the Block Tutorial.
+
+### Saving Attributes to Child Elements
+
+{% codetabs %}
+{% ES5 %}
+```js
+attributes: {
+ content: {
+ type: 'string',
+ source: 'html',
+ selector: 'p'
+ }
+},
+
+edit: function( props ) {
+ var updateFieldValue = function( val ) {
+ props.setAttributes( { content: val } );
+ }
+ return wp.element.createElement(
+ wp.components.TextControl,
+ {
+ label: 'My Text Field',
+ value: props.attributes.content,
+ onChange: updateFieldValue,
+
+ }
+ );
+},
+
+save: function( props ) {
+ return el( 'p', {}, props.attributes.content );
+},
+```
+{% ESNext %}
+```jsx
+attributes: {
+ content: {
+ type: 'string',
+ source: 'html',
+ selector: 'p'
+ }
+},
+
+edit: ( { attributes, setAttributes } ) => {
+ const updateFieldValue = ( val ) => {
+ setAttributes( { content: val } );
+ }
+ return ;
+},
+
+save: ( { attributes } ) => {
+ return
{ attributes.content }
;
+},
+```
+{% end %}
+
+### Saving Attributes via Serialization
+
+Ideally, the attributes saved should be included in the markup. However, there are times when this is not practical, so if no attribute source is specified the attribute is serialized and saved to the block's comment delimiter.
+
+This example could be for a dynamic block, such as the [Latest Posts block](https://github.com/WordPress/gutenberg/blob/master/packages/block-library/src/latest-posts/index.js), which renders the markup server-side. The save function is still required, however in this case it simply returns null since the block is not saving content from the editor.
+
+{% codetabs %}
+{% ES5 %}
+```js
+attributes: {
+ postsToShow: {
+ type: 'number',
+ }
+},
+
+edit: function( props ) {
+ return wp.element.createElement(
+ wp.components.TextControl,
+ {
+ label: 'Number Posts to Show',
+ value: props.attributes.postsToShow,
+ onChange: function( val ) {
+ props.setAttributes( { postsToShow: parseInt( val ) } );
+ },
+ }
+ );
+},
+
+save: function() {
+ return null;
+}
+```
+{% ESNext %}
+```jsx
+attributes: {
+ postsToShow: {
+ type: 'number',
+ }
+},
+
+edit: ( { attributes, setAttributes } ) => {
+ return {
+ setAttributes( { postsToShow: parseInt( val ) } );
+ }},
+ }
+ );
+},
+
+save: () => {
+ return null;
+}
+```
+{% end %}
+
+
+## Validation
+
+When the editor loads, all blocks within post content are validated to determine their accuracy in order to protect against content loss. This is closely related to the saving implementation of a block, as a user may unintentionally remove or modify their content if the editor is unable to restore a block correctly. During editor initialization, the saved markup for each block is regenerated using the attributes that were parsed from the post's content. If the newly-generated markup does not match what was already stored in post content, the block is marked as invalid. This is because we assume that unless the user makes edits, the markup should remain identical to the saved content.
+
+If a block is detected to be invalid, the user will be prompted to choose how to handle the invalidation:
+
+
+
+- **Overwrite**: Ignores the warning and treats the newly generated markup as correct. As noted in the behavior described above, this can result in content loss since it will overwrite the markup saved in post content.
+- **Convert to Classic**: Protects the original markup from the saved post content as correct. Since the block will be converted from its original type to the Classic block type, it will no longer be possible to edit the content using controls available for the original block type.
+- **Edit as HTML block**: Similar to _Convert to Classic_, this will protect the original markup from the saved post content and convert the block from its original type to the HTML block type, enabling the user to modify the HTML markup directly.
+
+### Validation FAQ
+
+**How do blocks become invalid?**
+
+The two most common sources of block invalidations are:
+
+1. A flaw in a block's code would result in unintended content modifications. See the question below on how to debug block invalidation as a plugin author.
+2. You or an external editor changed the HTML markup of the block in such a way that it is no longer considered correct.
+
+**I'm a plugin author. What should I do to debug why my blocks are being marked as invalid?**
+
+Before starting to debug, be sure to familiarize yourself with the validation step described above documenting the process for detecting whether a block is invalid. A block is invalid if its regenerated markup does not match what is saved in post content, so often this can be caused by the attributes of a block being parsed incorrectly from the saved content.
+
+If you're using [attribute sources](/docs/designers-developers/developers/block-api/block-attributes.md), be sure that attributes sourced from markup are saved exactly as you expect, and in the correct type (usually a `'string'` or `'number'`).
+
+When a block is detected as invalid, a warning will be logged into your browser's developer tools console. The warning will include specific details about the exact point at which a difference in markup occurred. Be sure to look closely at any differences in the expected and actual markups to see where problems are occurring.
+
+**I've changed my block's `save` behavior and old content now includes invalid blocks. How can I fix this?**
+
+Refer to the guide on [Deprecated Blocks](/docs/designers-developers/developers/block-api/block-deprecation.md) to learn more about how to accommodate legacy content in intentional markup changes.
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-registration.md b/gb-src/docs/designers-developers/developers/block-api/block-registration.md
new file mode 100644
index 0000000000000..2aff571710118
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-registration.md
@@ -0,0 +1,618 @@
+# Block Registration
+
+## `register_block_type`
+
+* **Type:** `Function`
+
+Every block starts by registering a new block type definition. To register, you use the `registerBlockType` function from the [`wp-blocks` package](/packages/blocks/README.md#registerBlockType). The function takes two arguments, a block `name` and a block configuration object.
+
+### Block Name
+
+* **Type:** `String`
+
+The name for a block is a unique string that identifies a block. Names have to be structured as `namespace/block-name`, where namespace is the name of your plugin or theme.
+
+```js
+// Registering my block with a unique name
+registerBlockType( 'my-plugin/book', {} );
+```
+
+*Note:* A block name can only contain lowercase alphanumeric characters and dashes, and must begin with a letter.
+
+*Note:* This name is used on the comment delimiters as ``. Those blocks provided by core don't include a namespace when serialized.
+
+### Block Configuration
+
+* **Type:** `Object` [ `{ key: value }` ]
+
+A block requires a few properties to be specified before it can be registered successfully. These are defined through a configuration object, which includes the following:
+
+#### Title
+
+* **Type:** `String`
+
+This is the display title for your block, which can be translated with our translation functions. The block inserter will show this name.
+
+```js
+// Our data object
+title: __( 'Book' )
+```
+
+#### Description (optional)
+
+* **Type:** `String`
+
+This is a short description for your block, which can be translated with our translation functions. This will be shown in the Block Tab in the Settings Sidebar.
+
+```js
+description: __( 'Block showing a Book card.' )
+```
+
+#### Category
+
+* **Type:** `String` [ common | formatting | layout | widgets | embed ]
+
+Blocks are grouped into categories to help users browse and discover them.
+
+The core provided categories are:
+
+* common
+* formatting
+* layout
+* widgets
+* embed
+
+```js
+// Assigning to the 'widgets' category
+category: 'widgets',
+```
+
+Plugins and Themes can also register [custom block categories](/docs/designers-developers/developers/filters/block-filters.md#managing-block-categories).
+
+#### Icon (optional)
+
+* **Type:** `String` | `Object`
+
+An icon property should be specified to make it easier to identify a block. These can be any of [WordPress' Dashicons](https://developer.wordpress.org/resource/dashicons/), or a custom `svg` element.
+
+```js
+// Specifying a dashicon for the block
+icon: 'book-alt',
+
+// Specifying a custom svg for the block
+icon: ,
+```
+
+**Note:** Custom SVG icons are automatically wrapped in the [`wp.components.SVG` component](/packages/components/src/primitives/svg/) to add accessibility attributes (`aria-hidden`, `role`, and `focusable`).
+
+An object can also be passed as icon, in this case, icon, as specified above, should be included in the src property.
+
+Besides src the object can contain background and foreground colors, this colors will appear with the icon when they are applicable e.g.: in the inserter.
+
+```js
+icon: {
+ // Specifying a background color to appear with the icon e.g.: in the inserter.
+ background: '#7e70af',
+ // Specifying a color for the icon (optional: if not set, a readable color will be automatically defined)
+ foreground: '#fff',
+ // Specifying a dashicon for the block
+ src: 'book-alt',
+} ,
+```
+
+#### Keywords (optional)
+
+* **Type:** `Array`
+
+Sometimes a block could have aliases that help users discover it while searching. For example, an `image` block could also want to be discovered by `photo`. You can do so by providing an array of terms (which can be translated).
+
+```js
+// Make it easier to discover a block with keyword aliases.
+// These can be localised so your keywords work across locales.
+keywords: [ __( 'image' ), __( 'photo' ), __( 'pics' ) ],
+```
+
+#### Styles (optional)
+
+* **Type:** `Array`
+
+Block styles can be used to provide alternative styles to block. It works by adding a class name to the block’s wrapper. Using CSS, a theme developer can target the class name for the style variation if it is selected.
+
+```js
+// Register block styles.
+styles: [
+ // Mark style as default.
+ {
+ name: 'default',
+ label: __( 'Rounded' ),
+ isDefault: true
+ },
+ {
+ name: 'outline',
+ label: __( 'Outline' )
+ },
+ {
+ name: 'squared',
+ label: __( 'Squared' )
+ },
+],
+```
+
+Plugins and Themes can also register [custom block style](/docs/designers-developers/developers/filters/block-filters.md#block-style-variations) for existing blocks.
+
+#### Attributes (optional)
+
+* **Type:** `Object`
+
+Attributes provide the structured data needs of a block. They can exist in different forms when they are serialized, but they are declared together under a common interface.
+
+```js
+// Specifying my block attributes
+attributes: {
+ cover: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'src',
+ },
+ author: {
+ type: 'string',
+ source: 'html',
+ selector: '.book-author',
+ },
+ pages: {
+ type: 'number',
+ },
+},
+```
+
+* **See: [Attributes](/docs/designers-developers/developers/block-api/block-attributes.md).**
+
+#### Transforms (optional)
+
+* **Type:** `Array`
+
+Transforms provide rules for what a block can be transformed from and what it can be transformed to. A block can be transformed from another block, a shortcode, a regular expression, a file or a raw DOM node.
+
+For example, a Paragraph block can be transformed into a Heading block. This uses the `createBlock` function from the [`wp-blocks` package](/packages/blocks/README.md#createBlock).
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ transform: function ( attributes ) {
+ return createBlock( 'core/heading', {
+ content: attributes.content,
+ } );
+ },
+ },
+ ]
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ transform: ( { content } ) => {
+ return createBlock( 'core/heading', {
+ content,
+ } );
+ },
+ },
+ ]
+},
+```
+{% end %}
+
+An existing shortcode can be transformed into its block counterpart.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'shortcode',
+ // Shortcode tag can also be an array of shortcode aliases
+ tag: 'caption',
+ attributes: {
+ // An attribute can be source from a tag attribute in the shortcode content
+ url: {
+ type: 'string',
+ source: 'attribute',
+ attribute: 'src',
+ selector: 'img',
+ },
+ // An attribute can be source from the shortcode attributes
+ align: {
+ type: 'string',
+ shortcode: function( attributes ) {
+ var align = attributes.named.align ? attributes.named.align : 'alignnone';
+ return align.replace( 'align', '' );
+ },
+ },
+ },
+ },
+ ]
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'shortcode',
+ // Shortcode tag can also be an array of shortcode aliases
+ tag: 'caption',
+ attributes: {
+ // An attribute can be source from a tag attribute in the shortcode content
+ url: {
+ type: 'string',
+ source: 'attribute',
+ attribute: 'src',
+ selector: 'img',
+ },
+ // An attribute can be source from the shortcode attributes
+ align: {
+ type: 'string',
+ shortcode: ( { named: { align = 'alignnone' } } ) => {
+ return align.replace( 'align', '' );
+ },
+ },
+ },
+ },
+ ]
+},
+
+```
+{% end %}
+
+A block can also be transformed into another block type. For example, a Heading block can be transformed into a Paragraph block.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ transform: function( attributes ) {
+ return createBlock( 'core/paragraph', {
+ content: attributes.content,
+ } );
+ },
+ },
+ ],
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ transform: ( { content } ) => {
+ return createBlock( 'core/paragraph', {
+ content,
+ } );
+ },
+ },
+ ],
+},
+```
+{% end %}
+
+In addition to accepting an array of known block types, the `blocks` option also accepts a "wildcard" (`"*"`). This allows for transformations which apply to _all_ block types (eg: all blocks can transform into `core/group`):
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'block',
+ blocks: [ '*' ], // wildcard - match any block
+ transform: function( attributes, innerBlocks ) {
+ // transform logic here
+ },
+ },
+ ],
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'block',
+ blocks: [ '*' ], // wildcard - match any block
+ transform: ( attributes, innerBlocks ) => {
+ // transform logic here
+ },
+ },
+ ],
+},
+```
+{% end %}
+
+
+A block with InnerBlocks can also be transformed from and to another block with InnerBlocks.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'some/block-with-innerblocks' ],
+ transform: function( attributes, innerBlocks ) {
+ return createBlock( 'some/other-block-with-innerblocks', attributes, innerBlocks );
+ },
+ },
+ ],
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'some/block-with-innerblocks' ],
+ transform: ( attributes, innerBlocks ) => {
+ return createBlock( 'some/other-block-with-innerblocks', attributes, innerBlocks);
+ },
+ },
+ ],
+},
+```
+{% end %}
+
+An optional `isMatch` function can be specified on a transform object. This provides an opportunity to perform additional checks on whether a transform should be possible. Returning `false` from this function will prevent the transform from being displayed as an option to the user.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ isMatch: function( attributes ) {
+ return attributes.isText;
+ },
+ transform: function( attributes ) {
+ return createBlock( 'core/paragraph', {
+ content: attributes.content,
+ } );
+ },
+ },
+ ],
+},
+```
+{% ESNext %}
+```js
+transforms: {
+ to: [
+ {
+ type: 'block',
+ blocks: [ 'core/paragraph' ],
+ isMatch: ( { isText } ) => isText,
+ transform: ( { content } ) => {
+ return createBlock( 'core/paragraph', {
+ content,
+ } );
+ },
+ },
+ ],
+},
+```
+{% end %}
+
+To control the priority with which a transform is applied, define a `priority` numeric property on your transform object, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+
+A file can be dropped into the editor and converted into a block with a matching transform.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'files',
+ isMatch: function ( files ) {
+ return files.length === 1;
+ },
+ // We define a lower priority (higher number) than the default of 10. This
+ // ensures that the File block is only created as a fallback.
+ priority: 15,
+ transform: function( files ) {
+ var file = files[ 0 ];
+ var blobURL = createBlobURL( file );
+
+ // File will be uploaded in componentDidMount()
+ return createBlock( 'core/file', {
+ href: blobURL,
+ fileName: file.name,
+ textLinkHref: blobURL,
+ } );
+ },
+ },
+ ]
+}
+```
+{% ESNext %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'files',
+ isMatch: ( files ) => files.length === 1,
+ // We define a lower priority (higher number) than the default of 10. This
+ // ensures that the File block is only created as a fallback.
+ priority: 15,
+ transform: ( files ) => {
+ const file = files[ 0 ];
+ const blobURL = createBlobURL( file );
+
+ // File will be uploaded in componentDidMount()
+ return createBlock( 'core/file', {
+ href: blobURL,
+ fileName: file.name,
+ textLinkHref: blobURL,
+ } );
+ },
+ },
+ ]
+}
+```
+{% end %}
+
+A prefix transform is a transform that will be applied if the user prefixes some text in e.g. the Paragraph block with a given pattern and a trailing space.
+
+{% codetabs %}
+{% ES5 %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'prefix',
+ prefix: '?',
+ transform: function( content ) {
+ return createBlock( 'my-plugin/question', {
+ content,
+ } );
+ },
+ },
+ ]
+}
+```
+{% ESNext %}
+```js
+transforms: {
+ from: [
+ {
+ type: 'prefix',
+ prefix: '?',
+ transform( content ) {
+ return createBlock( 'my-plugin/question', {
+ content,
+ } );
+ },
+ },
+ ]
+}
+```
+{% end %}
+
+
+#### parent (optional)
+
+* **Type:** `Array`
+
+Blocks are able to be inserted into blocks that use [`InnerBlocks`](https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/inner-blocks/README.md) as nested content. Sometimes it is useful to restrict a block so that it is only available as a nested block. For example, you might want to allow an 'Add to Cart' block to only be available within a 'Product' block.
+
+Setting `parent` lets a block require that it is only available when nested within the specified blocks.
+
+```js
+// Only allow this block when it is nested in a Columns block
+parent: [ 'core/columns' ],
+```
+
+#### supports (optional)
+
+*Some [block supports](#supports-optional) — for example, `anchor` or `className` — apply their attributes by adding additional props on the element returned by `save`. This will work automatically for default HTML tag elements (`div`, etc). However, if the return value of your `save` is a custom component element, you will need to ensure that your custom component handles these props in order for the attributes to be persisted.*
+
+* **Type:** `Object`
+
+Optional block extended support features. The following options are supported:
+
+- `align` (default `false`): This property adds block controls which allow to change block's alignment. _Important: It doesn't work with dynamic blocks yet._
+
+```js
+// Add the support for block's alignment (left, center, right, wide, full).
+align: true,
+// Pick which alignment options to display.
+align: [ 'left', 'right', 'full' ],
+```
+When supports align is used the block attributes definition is extended to include an align attribute with a string type.
+By default, no alignment is assigned to the block.
+The block can apply a default alignment by specifying its own align attribute with a default e.g.:
+```
+attributes: {
+ ...
+ align: {
+ type: 'string',
+ default: 'right'
+ },
+ ...
+}
+```
+
+- `alignWide` (default `true`): This property allows to enable [wide alignment](/docs/designers-developers/developers/themes/theme-support.md#wide-alignment) for your theme. To disable this behavior for a single block, set this flag to `false`.
+
+```js
+// Remove the support for wide alignment.
+alignWide: false,
+```
+
+- `anchor` (default `false`): Anchors let you link directly to a specific block on a page. This property adds a field to define an id for the block and a button to copy the direct link.
+
+```js
+// Add the support for an anchor link.
+anchor: true,
+```
+
+- `customClassName` (default `true`): This property adds a field to define a custom className for the block's wrapper.
+
+```js
+// Remove the support for the custom className.
+customClassName: false,
+```
+
+- `className` (default `true`): By default, the class `.wp-block-your-block-name` is added to the root element of your saved markup. This helps having a consistent mechanism for styling blocks that themes and plugins can rely on. If for whatever reason a class is not desired on the markup, this functionality can be disabled.
+
+```js
+// Remove the support for the generated className.
+className: false,
+```
+
+- `html` (default `true`): By default, a block's markup can be edited individually. To disable this behavior, set `html` to `false`.
+
+```js
+// Remove support for an HTML mode.
+html: false,
+```
+
+- `inserter` (default `true`): By default, all blocks will appear in the inserter. To hide a block so that it can only be inserted programmatically, set `inserter` to `false`.
+
+```js
+// Hide this block from the inserter.
+inserter: false,
+```
+
+- `multiple` (default `true`): A non-multiple block can be inserted into each post, one time only. For example, the built-in 'More' block cannot be inserted again if it already exists in the post being edited. A non-multiple block's icon is automatically dimmed (unclickable) to prevent multiple instances.
+
+```js
+// Use the block just once per post
+multiple: false,
+```
+
+- `reusable` (default `true`): A block may want to disable the ability of being converted into a reusable block.
+By default all blocks can be converted to a reusable block. If supports reusable is set to false, the option to convert the block into a reusable block will not appear.
+
+```js
+// Don't allow the block to be converted into a reusable block.
+reusable: false,
+```
+
diff --git a/gb-src/docs/designers-developers/developers/block-api/block-templates.md b/gb-src/docs/designers-developers/developers/block-api/block-templates.md
new file mode 100644
index 0000000000000..b2b384a96e211
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/block-api/block-templates.md
@@ -0,0 +1,133 @@
+# Templates
+
+A block template is defined as a list of block items. Such blocks can have predefined attributes, placeholder content, and be static or dynamic. Block templates allow to specify a default initial state for an editor session.
+
+The scope of templates include:
+
+- Setting a default state dynamically on the client. (like `defaultBlock`)
+- Registered as a default for a given post type.
+
+Planned additions:
+
+- Saved and assigned to pages as "page templates".
+- Defined in a `template.php` file or pulled from a custom post type (`wp_templates`) that is site specific.
+- As the equivalent of the theme hierarchy.
+
+## API
+
+Templates can be declared in JS or in PHP as an array of blockTypes (block name and optional attributes).
+
+The first example in PHP creates a template for posts that includes an image block to start, you can add as many or as few blocks to your template as needed.
+
+PHP example:
+
+```php
+template = array(
+ array( 'core/image' ),
+ );
+}
+add_action( 'init', 'myplugin_register_template' );
+```
+
+The following example in JavaScript creates a new block using [InnerBlocks](/packages/block-editor/src/components/inner-blocks/README.md) and templates, when inserted creates a set of blocks based off the template.
+
+```js
+const el = wp.element.createElement;
+const { registerBlockType } = wp.blocks;
+const { InnerBlocks } = wp.editor;
+
+const BLOCKS_TEMPLATE = [
+ [ 'core/image', {} ],
+ [ 'core/paragraph', { placeholder: 'Image Details' } ],
+];
+
+registerBlockType( 'myplugin/template', {
+ title: 'My Template Block',
+ category: 'widgets',
+ edit: ( props ) => {
+ return el( InnerBlocks, {
+ template: BLOCKS_TEMPLATE,
+ templateLock: false
+ });
+ },
+ save: ( props ) => {
+ return el( InnerBlocks.Content, {} );
+ },
+});
+```
+
+See the [Meta Block Tutorial](/docs/designers-developers/developers/tutorials/metabox/meta-block-5-finishing.md) for a full example of a template in use.
+
+## Custom Post types
+
+A custom post type can register its own template during registration:
+
+```php
+function myplugin_register_book_post_type() {
+ $args = array(
+ 'public' => true,
+ 'label' => 'Books',
+ 'show_in_rest' => true,
+ 'template' => array(
+ array( 'core/image', array(
+ 'align' => 'left',
+ ) ),
+ array( 'core/heading', array(
+ 'placeholder' => 'Add Author...',
+ ) ),
+ array( 'core/paragraph', array(
+ 'placeholder' => 'Add Description...',
+ ) ),
+ ),
+ );
+ register_post_type( 'book', $args );
+}
+add_action( 'init', 'myplugin_register_book_post_type' );
+```
+
+### Locking
+
+Sometimes the intention might be to lock the template on the UI so that the blocks presented cannot be manipulated. This is achieved with a `template_lock` property.
+
+```php
+function myplugin_register_template() {
+ $post_type_object = get_post_type_object( 'post' );
+ $post_type_object->template = array(
+ array( 'core/paragraph', array(
+ 'placeholder' => 'Add Description...',
+ ) ),
+ );
+ $post_type_object->template_lock = 'all';
+}
+add_action( 'init', 'myplugin_register_template' );
+```
+
+*Options:*
+
+- `all` — prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks.
+- `insert` — prevents inserting or removing blocks, but allows moving existing blocks.
+
+## Nested Templates
+
+Container blocks like the columns blocks also support templates. This is achieved by assigning a nested template to the block.
+
+```php
+$template = array(
+ array( 'core/paragraph', array(
+ 'placeholder' => 'Add a root-level paragraph',
+ ) ),
+ array( 'core/columns', array(), array(
+ array( 'core/column', array(), array(
+ array( 'core/image', array() ),
+ ) ),
+ array( 'core/column', array(), array(
+ array( 'core/paragraph', array(
+ 'placeholder' => 'Add a inner paragraph'
+ ) ),
+ ) ),
+ ) )
+);
+```
diff --git a/gb-src/docs/designers-developers/developers/data/README.md b/gb-src/docs/designers-developers/developers/data/README.md
new file mode 100644
index 0000000000000..7408d171144cf
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/data/README.md
@@ -0,0 +1,11 @@
+# Data Module Reference
+
+ - [**core**: WordPress Core Data](/docs/designers-developers/developers/data/data-core.md)
+ - [**core/annotations**: Annotations](/docs/designers-developers/developers/data/data-core-annotations.md)
+ - [**core/blocks**: Block Types Data](/docs/designers-developers/developers/data/data-core-blocks.md)
+ - [**core/block-editor**: The Block Editor’s Data](/docs/designers-developers/developers/data/data-core-block-editor.md)
+ - [**core/editor**: The Post Editor’s Data](/docs/designers-developers/developers/data/data-core-editor.md)
+ - [**core/edit-post**: The Editor’s UI Data](/docs/designers-developers/developers/data/data-core-edit-post.md)
+ - [**core/notices**: Notices Data](/docs/designers-developers/developers/data/data-core-notices.md)
+ - [**core/nux**: The NUX (New User Experience) Data](/docs/designers-developers/developers/data/data-core-nux.md)
+ - [**core/viewport**: The Viewport Data](/docs/designers-developers/developers/data/data-core-viewport.md)
\ No newline at end of file
diff --git a/gb-src/docs/designers-developers/developers/data/data-core-annotations.md b/gb-src/docs/designers-developers/developers/data/data-core-annotations.md
new file mode 100644
index 0000000000000..e71aefd27d212
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/data/data-core-annotations.md
@@ -0,0 +1,20 @@
+# Annotations
+
+Namespace: `core/annotations`.
+
+## Selectors
+
+
+
+Nothing to document.
+
+
+
+
+## Actions
+
+
+
+Nothing to document.
+
+
diff --git a/gb-src/docs/designers-developers/developers/data/data-core-block-editor.md b/gb-src/docs/designers-developers/developers/data/data-core-block-editor.md
new file mode 100644
index 0000000000000..f6f80cdfec535
--- /dev/null
+++ b/gb-src/docs/designers-developers/developers/data/data-core-block-editor.md
@@ -0,0 +1,1246 @@
+# The Block Editor’s Data
+
+Namespace: `core/block-editor`.
+
+## Selectors
+
+
+
+# **canInsertBlockType**
+
+Determines if the given block type is allowed to be inserted into the block list.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _blockName_ `string`: The name of the block type, e.g.' core/paragraph'.
+- _rootClientId_ `?string`: Optional root client ID of block list.
+
+_Returns_
+
+- `boolean`: Whether the given block type is allowed to be inserted.
+
+# **didAutomaticChange**
+
+Returns true if the last change was an automatic change, false otherwise.
+
+_Parameters_
+
+- _state_ `Object`: Global application state.
+
+_Returns_
+
+- `boolean`: Whether the last change was automatic.
+
+# **getAdjacentBlockClientId**
+
+Returns the client ID of the block adjacent one at the given reference
+startClientId and modifier directionality. Defaults start startClientId to
+the selected block, and direction as next block. Returns null if there is no
+adjacent block.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _startClientId_ `?string`: Optional client ID of block from which to search.
+- _modifier_ `?number`: Directionality multiplier (1 next, -1 previous).
+
+_Returns_
+
+- `?string`: Return the client ID of the block, or null if none exists.
+
+# **getBlock**
+
+Returns a block given its client ID. This is a parsed copy of the block,
+containing its `blockName`, `clientId`, and current `attributes` state. This
+is not the block's registration settings, which must be retrieved from the
+blocks module registration store.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block client ID.
+
+_Returns_
+
+- `Object`: Parsed block object.
+
+# **getBlockAttributes**
+
+Returns a block's attributes given its client ID, or null if no block exists with
+the client ID.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block client ID.
+
+_Returns_
+
+- `?Object`: Block attributes.
+
+# **getBlockCount**
+
+Returns the number of blocks currently present in the post.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _rootClientId_ `?string`: Optional root client ID of block list.
+
+_Returns_
+
+- `number`: Number of blocks in the post.
+
+# **getBlockHierarchyRootClientId**
+
+Given a block client ID, returns the root of the hierarchy from which the block is nested, return the block itself for root level blocks.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block from which to find root client ID.
+
+_Returns_
+
+- `string`: Root client ID
+
+# **getBlockIndex**
+
+Returns the index at which the block corresponding to the specified client
+ID occurs within the block order, or `-1` if the block does not exist.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block client ID.
+- _rootClientId_ `?string`: Optional root client ID of block list.
+
+_Returns_
+
+- `number`: Index at which block exists in order.
+
+# **getBlockInsertionPoint**
+
+Returns the insertion point, the index at which the new inserted block would
+be placed. Defaults to the last index.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+
+_Returns_
+
+- `Object`: Insertion point object with `rootClientId`, `index`.
+
+# **getBlockListSettings**
+
+Returns the Block List settings of a block, if any exist.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `?string`: Block client ID.
+
+_Returns_
+
+- `?Object`: Block settings of the block if set.
+
+# **getBlockMode**
+
+Returns the block's editing mode, defaulting to "visual" if not explicitly
+assigned.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block client ID.
+
+_Returns_
+
+- `Object`: Block editing mode.
+
+# **getBlockName**
+
+Returns a block's name given its client ID, or null if no block exists with
+the client ID.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block client ID.
+
+_Returns_
+
+- `string`: Block name.
+
+# **getBlockOrder**
+
+Returns an array containing all block client IDs in the editor in the order
+they appear. Optionally accepts a root client ID of the block list for which
+the order should be returned, defaulting to the top-level block order.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _rootClientId_ `?string`: Optional root client ID of block list.
+
+_Returns_
+
+- `Array`: Ordered client IDs of editor blocks.
+
+# **getBlockRootClientId**
+
+Given a block client ID, returns the root block from which the block is
+nested, an empty string for top-level blocks, or null if the block does not
+exist.
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _clientId_ `string`: Block from which to find root client ID.
+
+_Returns_
+
+- `?string`: Root client ID, if exists
+
+# **getBlocks**
+
+Returns all block objects for the current post being edited as an array in
+the order they appear in the post.
+
+Note: It's important to memoize this selector to avoid return a new instance
+on each call
+
+_Parameters_
+
+- _state_ `Object`: Editor state.
+- _rootClientId_ `?string`: Optional root client ID of block list.
+
+_Returns_
+
+- `Array