From 7779f79b844ed9ce13bdbddebeff88df0d5c7628 Mon Sep 17 00:00:00 2001
From: Riad Benguella <benguella@gmail.com>
Date: Mon, 10 Jul 2017 19:33:29 +0200
Subject: [PATCH] Documentation: Try a new custom documentation tool to rule
 them all (#1786)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Documentation: Try a new custom documentation tool to rule them all

* Documentation: Adding Previous/Next Page links

* Documentation: Parsing markdown blocks (code blocks)

* Documentation: Fix the doc tool title and metadata

* Documentation: Drop dashicon expand icon for now

* Documentation: Drop unnecessary markup from the documentation tool

* Documentation: Drop unnecessary folder nesting

* Documentation: Fix the doc tools eslint issues

* Documentation: Simplify the CLI API and rename the folder to glutenberg

* Documentation: SEO friendly using React-Snapshots

* Unignore docs from ESLint

Previously contained old prototypes, since removed.

* Documentation: Style active code tabs

* Documentation: Adding heading anchors

* Documentation: Overwriting Storybook with the new docs

* Documentation: Rename "glutenberg" to "docutron"

* Enable HTML in Markdown parser

* Documentation: reset scroll on page change

* Add initial pass at documentation (blocks tutorial, glossary)

* Retitle FAQ as Frequently Asked Questions

* Documentation: Better Prism integration

* Include coding guidelines and design principles in docs

* Fix links in reference landing page

* Include coding guidelines and design principles in docs

* Correct ESNext plugin namespace

* Documentation: Deploy only on master

* Move page title to header component

* Extend ESLint configuration to restore React pragma

* Renove ESLint ignore for unused vars

* Remember preference on code tabs

* Documentation: Fix indentation in CRA files

* Documentation: Filter Documentation Menu

* Remove lingering references to storybook

* Ignore node_modules everywhere for ESLint

* Move Docutron specific env settings to docutron configuration

* Fix Travis before_deploy docs build script name

* Move markdown-it-anchor dependency to docutron

* Replace markdown-it-anchor with 'markdown-it-toc-and-anchor

* Use store to get and set tabs preference

Avoid dependency on window for prerendering

* Fix incorrectly relative external links

* Hide anchor link in top-level heading

* Generate files as index.html in nested directory

* Documentation: Support overriding the default docs folder

* Documentation: Using the External/Internal dependencies comments

* Fix references to accompanying examples plugin

* Specify Docutron version

🤖 Recalibrating mainframe...

* Documentation: Drop eslint-disable-comment

* Use GitHub Pages for publishing documentation

* Extract components to separate files

* Rename components as lowercase

* Fix typo in stylesheet ESNext example

* Fix import reference to app component

Regression from rename

* Apply rel on navigation links

* Remove service worker registration

May be "nice to have", but not intending to support or maintain

* Rename extendsConfig to extendConfig

Consistency with named file

* Prevent search form submission

Filter results updated live
---
 .eslintignore                                 |   2 +-
 .eslintrc.json                                |   4 +-
 .gitignore                                    |   1 -
 .storybook/addons.js                          |   2 -
 .storybook/config.js                          |  43 -----
 .storybook/stories/coding-guidelines.js       |  15 --
 .storybook/stories/contributing.js            |  15 --
 .storybook/stories/design.js                  |  15 --
 .storybook/stories/intro.js                   |  15 --
 .storybook/style.scss                         |  52 ------
 .storybook/webpack.config.js                  |  35 ----
 .travis.yml                                   |  14 +-
 _config.yml                                   |   1 -
 blocks/story/index.js                         |  15 --
 components/button/story/index.js              |  44 -----
 components/higher-order/story/index.js        |  15 --
 .../with-instance-id/story/index.js           |  15 --
 components/story/index.js                     |  15 --
 docs/attribute-matchers.md                    |  62 +++++++
 docs/blocks-basic.md                          |  83 ++++++++++
 docs/blocks-controls.md                       | 152 ++++++++++++++++++
 docs/blocks-editable.md                       | 114 +++++++++++++
 docs/blocks-stylesheet.md                     | 105 ++++++++++++
 docs/blocks.md                                |   7 +
 docs/coding-guidelines.md                     |   3 +-
 docs/design.md                                |   5 +-
 docs/faq.md                                   |   4 +-
 docs/glossary.md                              |  15 ++
 docs/index.js                                 |  86 ++++++++++
 docs/readme.md                                |  13 ++
 docs/reference.md                             |   7 +
 docutron/.eslintrc.json                       |  10 ++
 docutron/.gitignore                           |  21 +++
 docutron/bin/build.js                         |  25 +++
 docutron/bin/cli.js                           |  20 +++
 docutron/bin/helpers/extend-webpack-config.js |  36 +++++
 docutron/bin/start.js                         |  25 +++
 docutron/package.json                         |  21 +++
 docutron/public/index.html                    |  43 +++++
 docutron/public/manifest.json                 |   8 +
 docutron/src/components/app.js                |  37 +++++
 docutron/src/components/header.js             |  43 +++++
 docutron/src/components/markdown-content.js   |  31 ++++
 docutron/src/components/menu-item.js          |  44 +++++
 docutron/src/components/page.js               |  49 ++++++
 docutron/src/components/sidebar.js            |  73 +++++++++
 docutron/src/components/tabs.js               |  71 ++++++++
 docutron/src/config/index.js                  |  49 ++++++
 docutron/src/index.js                         |  19 +++
 docutron/src/markdown/index.js                |  61 +++++++
 docutron/src/styles/main.css                  |  29 ++++
 editor/story/index.js                         |  15 --
 element/story/index.js                        |  15 --
 i18n/story/index.js                           |  15 --
 package.json                                  |  10 +-
 55 files changed, 1380 insertions(+), 364 deletions(-)
 delete mode 100644 .storybook/addons.js
 delete mode 100644 .storybook/config.js
 delete mode 100644 .storybook/stories/coding-guidelines.js
 delete mode 100644 .storybook/stories/contributing.js
 delete mode 100644 .storybook/stories/design.js
 delete mode 100644 .storybook/stories/intro.js
 delete mode 100644 .storybook/style.scss
 delete mode 100644 .storybook/webpack.config.js
 delete mode 100644 _config.yml
 delete mode 100644 blocks/story/index.js
 delete mode 100644 components/button/story/index.js
 delete mode 100644 components/higher-order/story/index.js
 delete mode 100644 components/higher-order/with-instance-id/story/index.js
 delete mode 100644 components/story/index.js
 create mode 100644 docs/attribute-matchers.md
 create mode 100644 docs/blocks-basic.md
 create mode 100644 docs/blocks-controls.md
 create mode 100644 docs/blocks-editable.md
 create mode 100644 docs/blocks-stylesheet.md
 create mode 100644 docs/blocks.md
 create mode 100644 docs/glossary.md
 create mode 100644 docs/index.js
 create mode 100644 docs/readme.md
 create mode 100644 docs/reference.md
 create mode 100644 docutron/.eslintrc.json
 create mode 100644 docutron/.gitignore
 create mode 100644 docutron/bin/build.js
 create mode 100755 docutron/bin/cli.js
 create mode 100644 docutron/bin/helpers/extend-webpack-config.js
 create mode 100644 docutron/bin/start.js
 create mode 100644 docutron/package.json
 create mode 100644 docutron/public/index.html
 create mode 100644 docutron/public/manifest.json
 create mode 100644 docutron/src/components/app.js
 create mode 100644 docutron/src/components/header.js
 create mode 100644 docutron/src/components/markdown-content.js
 create mode 100644 docutron/src/components/menu-item.js
 create mode 100644 docutron/src/components/page.js
 create mode 100644 docutron/src/components/sidebar.js
 create mode 100644 docutron/src/components/tabs.js
 create mode 100644 docutron/src/config/index.js
 create mode 100644 docutron/src/index.js
 create mode 100644 docutron/src/markdown/index.js
 create mode 100644 docutron/src/styles/main.css
 delete mode 100644 editor/story/index.js
 delete mode 100644 element/story/index.js
 delete mode 100644 i18n/story/index.js

diff --git a/.eslintignore b/.eslintignore
index 84cf66469e19d..164ae5ac2c7ef 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,4 @@
 build
 coverage
-docs
 vendor
+node_modules
diff --git a/.eslintrc.json b/.eslintrc.json
index 76fc971b5fea4..a1e9739eda4b4 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -6,14 +6,14 @@
 		"plugin:react/recommended",
 		"plugin:jsx-a11y/recommended",
 		"plugin:jest/recommended"
-  ],
+	],
 	"env": {
 		"browser": false,
 		"es6": true,
 		"node": true,
 		"mocha": true,
 		"jest/globals": true
-  },
+	},
 	"parserOptions": {
 		"sourceType": "module",
 		"ecmaFeatures": {
diff --git a/.gitignore b/.gitignore
index 69f1023725d88..11169828c8085 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
 build
 coverage
 node_modules
-storybook-static
 gutenberg.zip
 
 # Directories/files that may appear in your environment
diff --git a/.storybook/addons.js b/.storybook/addons.js
deleted file mode 100644
index 5659100510d17..0000000000000
--- a/.storybook/addons.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import '@storybook/addon-knobs/register';
-import '@storybook/addon-options/register';
diff --git a/.storybook/config.js b/.storybook/config.js
deleted file mode 100644
index 35d9b93b2b298..0000000000000
--- a/.storybook/config.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * External dependencies
- */
-import 'prismjs';
-import { configure, setAddon } from '@storybook/react';
-import infoAddon from '@storybook/addon-info';
-import { setOptions } from '@storybook/addon-options';
-
-/**
- * Internal dependencies
- */
-import * as element from 'element';
-import './style.scss';
-
-function loadStories() {
-	window.wp = { ...window.wp, element };
-	require( './stories/intro' );
-	require( './stories/contributing' );
-	require( './stories/coding-guidelines' );
-	require( './stories/design' );
-	require( '../i18n/story' );
-	require( '../element/story' );
-	require( '../blocks/story' );
-	require( '../editor/story' );
-	require( '../components/story' );
-	require( '../components/button/story' );
-	require( '../components/higher-order/story' );
-	require( '../components/higher-order/with-instance-id/story' );
-}
-
-setOptions( {
-	name: 'Gutenberg',
-	url: 'https://github.com/WordPress/gutenberg',
-	goFullScreen: false,
-	showLeftPanel: true,
-	showDownPanel: true,
-	showSearchBox: false,
-	downPanelInRight: true,
-	sortStoriesByKind: false,
-} );
-setAddon( infoAddon );
-
-configure( loadStories, module );
diff --git a/.storybook/stories/coding-guidelines.js b/.storybook/stories/coding-guidelines.js
deleted file mode 100644
index 85ec888d93f41..0000000000000
--- a/.storybook/stories/coding-guidelines.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../../docs/coding-guidelines.md';
-
-storiesOf( 'Gutenberg', module )
-	.addDecorator( withKnobs )
-	.add( 'Coding Guidelines', () => <ReactMarkdown source={ readme } /> );
diff --git a/.storybook/stories/contributing.js b/.storybook/stories/contributing.js
deleted file mode 100644
index aa11a3a7a3ffc..0000000000000
--- a/.storybook/stories/contributing.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../../CONTRIBUTING.md';
-
-storiesOf( 'Gutenberg', module )
-	.addDecorator( withKnobs )
-	.add( 'Contributing', () => <ReactMarkdown source={ readme } /> );
diff --git a/.storybook/stories/design.js b/.storybook/stories/design.js
deleted file mode 100644
index c2224d84af8c3..0000000000000
--- a/.storybook/stories/design.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../../docs/design.md';
-
-storiesOf( 'Gutenberg', module )
-	.addDecorator( withKnobs )
-	.add( 'Design', () => <ReactMarkdown source={ readme } /> );
diff --git a/.storybook/stories/intro.js b/.storybook/stories/intro.js
deleted file mode 100644
index 6798f125317c8..0000000000000
--- a/.storybook/stories/intro.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../../README.md';
-
-storiesOf( 'Gutenberg', module )
-	.addDecorator( withKnobs )
-	.add( 'Intro', () => <ReactMarkdown source={ readme } /> );
diff --git a/.storybook/style.scss b/.storybook/style.scss
deleted file mode 100644
index 59e6f6366a2c5..0000000000000
--- a/.storybook/style.scss
+++ /dev/null
@@ -1,52 +0,0 @@
-@import url( 'https://wordpress.org/wp-admin/load-styles.php?c=0&dir=ltr&load%5B%5D=common,buttons,dashicons,forms' );
-
-html {
-	height: auto;
-}
-
-body {
-	background: none;
-	padding: 20px;
-	margin: 0;
-}
-
-p, h1, h2, li {
-	// Match Info Addon Styling
-	font-family: -apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
-	color: rgb(68, 68, 68);
-	-webkit-font-smoothing: antialiased;
-	font-weight: 400;
-	line-height: 1.45;
-	font-size: 15px;
-}
-
-h1 {
-	font-size: 2em;
-	margin: .67em 0;
-	font-weight: 600;
-}
-
-h2 {
-	margin: 0px 0px 10px;
-	padding: 0px;
-	font-weight: 400;
-	font-size: 22px;
-}
-
-pre {
-	font-size: 0.88em;
-	font-family: Menlo, Monaco, "Courier New", monospace;
-	background-color: rgb(250, 250, 250);
-	padding: 0.5rem;
-	line-height: 1.5;
-	overflow-x: auto !important; // Overrides the Info Addon Styling
-
-	&:before, &:after {
-		background: red;
-	}
-}
-
-code {
-	font-family: Menlo, Monaco, "Courier New", monospace;
-	background-color: rgb(250, 250, 250);
-}
diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js
deleted file mode 100644
index e24e1bd60eb89..0000000000000
--- a/.storybook/webpack.config.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const config = require( '../webpack.config' );
-const webpack = require( 'webpack' );
-config.module.rules = [
-	// Exclude the sass loader to override it
-	...config.module.rules.filter( ( rule ) => ! rule.test.test( '.scss' ) ),
-	{
-		test: /\.md/,
-		use: 'raw-loader',
-	},
-	{
-		test: /\.scss$/,
-		use: [
-			{ loader: 'style-loader' },
-			{ loader: 'css-loader' },
-			{ loader: 'postcss-loader' },
-			{
-				loader: 'sass-loader',
-				query: {
-					includePaths: [ 'editor/assets/stylesheets' ],
-					data: '@import "variables"; @import "mixins"; @import "animations";@import "z-index";',
-					outputStyle: 'production' === process.env.NODE_ENV ?
-						'compressed' : 'nested',
-				},
-			},
-		],
-	},
-];
-config.externals = [];
-
-// Exclude Uglify Plugin to avoid breaking the React Components Display Name
-config.plugins = config.plugins.filter( plugin => {
-	return plugin.constructor !== webpack.optimize.UglifyJsPlugin;
-} );
-
-module.exports = config;
diff --git a/.travis.yml b/.travis.yml
index 4e162f598ffcd..13199ddfc7b7a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -80,10 +80,16 @@ script:
     fi
 
 before_deploy:
-  - npm install && npm run build-storybook
+  - npm install
+  - cd docutron
+  - npm install
+  - cd ../
+  - npm run docs-build
 
 deploy:
-  provider: surge
-  project: ./storybook-static/
-  domain: gutenberg-devdoc.surge.sh
+  provider: pages
+  local_dir: ./docutron/build/
   skip_cleanup: true
+  github_token: $GITHUB_TOKEN
+  on:
+    branch: master
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index c4192631f25b3..0000000000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/blocks/story/index.js b/blocks/story/index.js
deleted file mode 100644
index ff05da78b4323..0000000000000
--- a/blocks/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Modules', module )
-	.addDecorator( withKnobs )
-	.add( 'Blocks', () => <ReactMarkdown source={ readme } /> );
diff --git a/components/button/story/index.js b/components/button/story/index.js
deleted file mode 100644
index 5e05caf4bc3a8..0000000000000
--- a/components/button/story/index.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { text, boolean, withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import Button from '../';
-import readme from '../README.md';
-
-const decorateWordPressUI = ( story ) => (
-	<div className="wp-core-ui">
-		{ story() }
-	</div>
-);
-
-const defaultInfoConfig = {
-	header: true,
-	inline: true,
-	propTables: false,
-	styles: ( style ) => ( { ...style, header: { ...style.header, h1: { display: 'none' } } } ),
-};
-
-storiesOf( 'Components', module )
-	.addDecorator( decorateWordPressUI )
-	.addDecorator( withKnobs )
-	.addWithInfo(
-		'Button',
-		( <ReactMarkdown source={ readme } /> ),
-		() => (
-			<Button
-				isPrimary={ boolean( 'isPrimary', true ) }
-				isLarge={ boolean( 'isLarge', false ) }
-				isToggled={ boolean( 'isToggled', false ) }
-				disabled={ boolean( 'disabled', false ) }
-			>
-				{ text( 'Label', 'Default Label' ) }
-			</Button>
-		),
-		defaultInfoConfig
-	);
diff --git a/components/higher-order/story/index.js b/components/higher-order/story/index.js
deleted file mode 100644
index f16f59add5f3a..0000000000000
--- a/components/higher-order/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Higher Order Components', module )
-	.addDecorator( withKnobs )
-	.add( 'Intro', () => <ReactMarkdown source={ readme } /> );
diff --git a/components/higher-order/with-instance-id/story/index.js b/components/higher-order/with-instance-id/story/index.js
deleted file mode 100644
index a2f498b303395..0000000000000
--- a/components/higher-order/with-instance-id/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Higher Order Components', module )
-	.addDecorator( withKnobs )
-	.add( 'withInstanceId', () => <ReactMarkdown source={ readme } /> );
diff --git a/components/story/index.js b/components/story/index.js
deleted file mode 100644
index bfc34c003f743..0000000000000
--- a/components/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Components', module )
-	.addDecorator( withKnobs )
-	.add( 'Intro', () => <ReactMarkdown source={ readme } /> );
diff --git a/docs/attribute-matchers.md b/docs/attribute-matchers.md
new file mode 100644
index 0000000000000..69668484b7c60
--- /dev/null
+++ b/docs/attribute-matchers.md
@@ -0,0 +1,62 @@
+# Attribute Matchers
+
+Attribute matchers are used to define the strategy by which 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.
+
+Each matcher accepts an optional selector as the first argument. If a selector is specified, the matcher behavior will be run against the corresponding element(s) contained within the block. Otherwise it will be run against the block's root node.
+
+Under the hood, attribute matchers are a superset of functionality provided by [hpq](https://github.com/aduth/hpq), a small library used to parse and query HTML markup into an object shape. In an object of attributes matchers, you can name the keys as you see fit. The resulting object will assign as a value to each key the result of its attribute matcher.
+
+## Common Matchers
+
+### `attr`
+
+Use `attr` 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: attr( 'img', 'src' )
+}
+// { "url": "https://lorempixel.com/1200/800/" }
+```
+
+### `children`
+
+Use `children` to extract child nodes of the matched element, returned as an array of virtual elements. This is most commonly used in combination with the `Editable` component.
+
+_Example_: Extract child nodes from a paragraph of rich text.
+
+```js
+{
+	content: children( 'p' )	
+}
+// {
+//   "content": [
+//     "Vestibulum eu ",
+//     { "type": "strong", "children": "tortor" }, 
+//     " vel urna."
+//   ]
+// }
+```
+
+### `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 matchers.
+
+_Example_: Extract `src` and `alt` from each image element in the block's markup.
+
+```js
+{
+	images: query( 'img', {
+		url: attr( 'src' )
+		alt: attr( 'alt' )
+	} )
+}
+// {
+//   "images": [ 
+//     { "url": "https://lorempixel.com/1200/800/", "alt": "large image" },
+//     { "url": "https://lorempixel.com/50/50/", "alt": "small image" }
+//   ]
+// }
+```
diff --git a/docs/blocks-basic.md b/docs/blocks-basic.md
new file mode 100644
index 0000000000000..9afe9cacac5e7
--- /dev/null
+++ b/docs/blocks-basic.md
@@ -0,0 +1,83 @@
+# Writing Your First Block Type
+
+To keep things simple for our first example, let's create a new block type which displays a styled message in a post. At this point, we won't allow the user to edit the message. We'll learn more about editable fields in later sections.
+
+Blocks containing static content are implemented entirely in JavaScript using the `registerBlockType` function. This function is responsible for specifying the blueprint of a block, describing the behaviors necessary for the editor to understand how it appears, changes when edited, and is ultimately saved in the post's content.
+
+## Enqueueing Block Scripts
+
+While the block type itself is implemented in JavaScript, you'll need to use the `enqueue_block_editor_assets` [WordPress action](https://codex.wordpress.org/Glossary#Action) to have your scripts included in the editor. This is similar to the [`wp_enqueue_scripts` action](https://developer.wordpress.org/reference/hooks/wp_enqueue_scripts/), but specifically targeting editor scripts and styles.
+
+```php
+<?php
+
+function gutenberg_boilerplate_enqueue_block_editor_assets() {
+	wp_enqueue_script(
+		'gutenberg-boilerplate-es5-step01',
+		plugins_url( 'step-01/block.js', __FILE__ ),
+		array( 'wp-blocks', 'wp-element' )
+	);
+}
+add_action( 'enqueue_block_editor_assets', 'gutenberg_boilerplate_enqueue_block_editor_assets' );
+```
+
+Note the two script dependencies:
+
+- __`wp-blocks`__ includes block type registration and related functions
+- __`wp-element`__ includes the [WordPress Element abstraction](https://github.com/WordPress/gutenberg/tree/master/element) for describing the structure of your blocks
+
+## Registering the Block
+
+With the script enqueued, let's look at the implementation of the block itself:
+
+{% codetabs %}
+{% ES5 %}
+```js
+var el = wp.element.createElement,
+	registerBlockType = wp.blocks.registerBlockType,
+	blockStyle = { backgroundColor: '#900', color: '#fff', padding: '20px' };
+
+registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-01', {
+	title: 'Hello World (Step 1)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	edit: function() {
+		return el( 'p', { style: blockStyle }, 'Hello editor.' );
+	},
+
+	save: function() {
+		return el( 'p', { style: blockStyle }, 'Hello saved content.' );
+	},
+} );
+```
+{% ESNext %}
+```js
+const { registerBlockType } = wp.blocks;
+const blockStyle = { backgroundColor: '#900', color: '#fff', padding: '20px' };
+
+registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-01', {
+	title: 'Hello World (Step 1)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	edit() {
+		return <p style={ blockStyle }>Hello editor.</p>;
+	},
+
+	save() {
+		return <p style={ blockStyle }>Hello saved content.</p>;
+	},
+} );
+```
+{% end %}
+
+Once a block is registered, you should immediately see that it becomes available as an option in the editor inserter dialog, using values from `title`, `icon`, and `category` to organize its display. You can choose an icon from any included in the built-in [Dashicons icon set](https://developer.wordpress.org/resource/dashicons/).
+
+A block name must be prefixed with a namespace specific to your plugin. This helps prevent conflicts when more than one plugin registers a block with the same name.
+
+The `edit` and `save` functions describe the structure of your block in the context of the editor and the saved content respectively. While the difference is not obvious in this simple example, in the following sections we'll explore how these are used to enable customization of the block in the editor preview.
diff --git a/docs/blocks-controls.md b/docs/blocks-controls.md
new file mode 100644
index 0000000000000..bb5699324c579
--- /dev/null
+++ b/docs/blocks-controls.md
@@ -0,0 +1,152 @@
+# Block Controls: Toolbars and Inspector
+
+To simplify block customization and ensure a consistent experience for users, there are a number of built-in UI patterns to help generate the editor preview. Like with the `Editable` component covered in the previous chapter, the `wp.blocks` global includes a few other common components to render editing interfaces. In this chapter, we'll explore toolbars and the block inspector.
+
+## Toolbar
+
+<img src="https://cldup.com/jUslj672CK.png" width="391" height="79" alt="toolbar">
+
+When the user selects a block, a number of control buttons may be shown in a toolbar above the selected block. Some of these block-level controls are included automatically if the editor is able to transform the block to another type, or if the focused element is an Editable component.
+
+You can also customize the toolbar to include controls specific to your block type. If the return value of your block type's `edit` function includes a `BlockControls` element, those controls will be shown in the selected block's toolbar.
+
+{% codetabs %}
+{% ES5 %}
+```js
+var el = wp.element.createElement,
+	registerBlockType = wp.blocks.registerBlockType,
+	Editable = wp.blocks.Editable,
+	BlockControls = wp.blocks.BlockControls,
+	AlignmentToolbar = wp.blocks.AlignmentToolbar,
+	children = wp.blocks.query.children;
+
+registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-04', {
+	title: 'Hello World (Step 4)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	attributes: {
+		content: children( 'p' ),
+	},
+
+	edit: function( props ) {
+		var content = props.attributes.content,
+			alignment = props.attributes.alignment,
+			focus = props.focus;
+
+		function onChangeContent( newContent ) {
+			props.setAttributes( { content: newContent } );
+		}
+
+		function onChangeAlignment( newAlignment ) {
+			props.setAttributes( { alignment: newAlignment } );
+		}
+
+		return [
+			!! focus && el(
+				BlockControls,
+				{ key: 'controls' },
+				el(
+					AlignmentToolbar,
+					{
+						value: alignment,
+						onChange: onChangeAlignment
+					}
+				)
+			),
+			el(
+				Editable,
+				{
+					key: 'editable',
+					tagName: 'p',
+					className: props.className,
+					style: { textAlign: alignment },
+					onChange: onChangeContent,
+					value: content,
+					focus: focus,
+					onFocus: props.setFocus
+				}
+			)
+		];
+	},
+
+	save: function( props ) {
+		var content = props.attributes.content;
+
+		return el( 'p', { className: props.className }, content );
+	},
+} );
+```
+{% ESNext %}
+```js
+const {
+	registerBlockType,
+	Editable,
+	BlockControls,
+	AlignmentToolbar,
+	query
+} = wp.blocks;
+const { children } = children;
+
+registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-04', {
+	title: 'Hello World (Step 4)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	attributes: {
+		content: children( 'p' ),
+	},
+
+	edit( { attributes, setAttributes, focus } ) {
+		const { content, alignment } = attributes;
+
+		function onChangeContent( newContent ) {
+			setAttributes( { content: newContent } );
+		}
+
+		function onChangeAlignment( newAlignment ) {
+			setAttributes( { alignment: newAlignment } );
+		}
+
+		return [
+			!! focus && (
+				<BlockControls key="controls">
+					<AlignmentToolbar
+						value={ alignment }
+						onChange={ onChangeAlignment }
+					/>
+				</BlockControls>
+			),
+			<Editable
+				key="editable"
+				tagName="p"
+				className={ props.className }
+				style={ { textAlign: alignment } }
+				onChange={ onChangeContent }
+				value={ content }
+				focus={ focus }
+				onFocus={ props.setFocus }
+			/>
+		];
+	},
+
+	save( { attributes, className } ) {
+		const { content } = attributes;
+
+		return <p className={ className }>{ content }</p>;
+	},
+} );
+```
+{% end %}
+
+Note that you should only include `BlockControls` if the block is currently selected. We must test that the `focus` value is truthy before rendering the element, otherwise you will inadvertently cause controls to be shown for the incorrect block type.
+
+## Inspector
+
+While the toolbar area is useful for displaying controls to toggle attributes of a block, sometimes you will find that you need more screen space for form fields. The inspector region is shown in place of the post settings sidebar when a block is selected.
+
+Similar to rendering a toolbar, if you include an `InspectorControls` element in the return value of your block type's `edit` function, those controls will be shown in the inspector region.
diff --git a/docs/blocks-editable.md b/docs/blocks-editable.md
new file mode 100644
index 0000000000000..c0b40f2a29076
--- /dev/null
+++ b/docs/blocks-editable.md
@@ -0,0 +1,114 @@
+# Introducing Attributes and Editable Fields
+
+Our example block is still not very interesting because it lacks options to customize the appearance of the message. In this section, we will implement an editable field allowing the user to specify their own message. Before doing so, it's important to understand how the state of a block (its "attributes") is maintained and changed over time.
+
+## Attributes
+
+Until now, the `edit` and `save` functions have returned a simple representation of a paragraph element. We also learned how these functions are responsible for _describing_ the structure of the block's appearance. If the user changes a block, this structure may need to change. To achieve this, the state of a block is maintained throughout the editing session as a plain JavaScript object, and when an update occurs, the `edit` function is invoked again. Put another way: __the output of a block is a function of its attributes__.
+
+One challenge of maintaining the representation of a block as a JavaScript object is that we must be able to extract this object again from the saved content of a post. This is achieved with the block type's `attribute` property:
+
+{% codetabs %}
+{% ES5 %}
+```js
+var el = wp.element.createElement,
+	registerBlockType = wp.blocks.registerBlockType,
+	Editable = wp.blocks.Editable,
+	children = wp.blocks.query.children;
+
+registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-03', {
+	title: 'Hello World (Step 3)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	attributes: {
+		content: children( 'p' ),
+	},
+
+	edit: function( props ) {
+		var content = props.attributes.content,
+			focus = props.focus;
+
+		function onChangeContent( newContent ) {
+			props.setAttributes( { content: newContent } );
+		}
+
+		return el(
+			Editable,
+			{
+				tagName: 'p',
+				className: props.className,
+				onChange: onChangeContent,
+				value: content,
+				focus: focus,
+				onFocus: props.setFocus
+			}
+		);
+	},
+
+	save: function( props ) {
+		var content = props.attributes.content;
+
+		return el( 'p', { className: props.className }, content );
+	},
+} );
+```
+{% ESNext %}
+```js
+const { registerBlockType, Editable, query } = wp.blocks;
+const { children } = query;
+
+registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-03', {
+	title: 'Hello World (Step 3)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	attributes: {
+		content: children( 'p' ),
+	},
+
+	edit( { attributes, setAttributes, focus, className } ) {
+		const { content } = attributes;
+
+		function onChangeContent( newContent ) {
+			setAttributes( { content: newContent } );
+		}
+
+		return (
+			<Editable
+				tagName="p"
+				className={ className }
+				onChange={ onChangeContent }
+				value={ content }
+				focus={ focus }
+				onFocus={ props.setFocus }
+			/>
+		);
+	},
+
+	save( { attributes, className } ) {
+		const { content } = attributes;
+
+		return <p className={ className }>{ content }</p>;
+	},
+} );
+```
+{% end %}
+
+When registering a new block type, the `attributes` property describes the shape of the attributes object you'd like to receive in the `edit` and `save` functions. Each value is a [matcher function]() to find the desired value from the markup of the block.
+
+In the code snippet above, when loading the editor, we will extract the `content` value as the children of the paragraph element in the saved post's markup.
+
+## Components and the `Editable` Component
+
+Earlier examples used the `createElement` function to create DOM nodes, but it's also possible to encapsulate this behavior into ["components"](). This abstraction helps as a pattern to share common behaviors and to hide complexity into self-contained units. There are a number of components available to use in implementing your blocks. You can see one such component in the snippet above: the [`Editable` component]().
+
+The `Editable` component can be considered as a super-powered `textarea` element, enabling rich content editing including bold, italics, hyperlinks, etc. It is not too much unlike the single editor region of the legacy post editor, and is in fact powered by the same TinyMCE library.
+
+Implementing this behavior as a component enables you as the block implementer to be much more granular about editable fields. Your block may not need `Editable` at all, or it may need many independent `Editable` elements, each operating on a subset of the overall block state.
+
+Because `Editable` allows for nested nodes, you'll most often use it in conjunction with the `children` attribute matcher when extracting the value from saved content.
diff --git a/docs/blocks-stylesheet.md b/docs/blocks-stylesheet.md
new file mode 100644
index 0000000000000..9fc07fff8ba11
--- /dev/null
+++ b/docs/blocks-stylesheet.md
@@ -0,0 +1,105 @@
+# Applying Styles From a Stylesheet
+
+In the previous section, the block had applied its own styles by an inline `style` attribute. While this might be adequate for very simple components, you will quickly find that it becomes easier to write your styles by extracting them to a separate stylesheet file.
+
+The editor will automatically generate a class name for each block type to simplify styling. It can be accessed from the object argument passed to the edit and save functions:
+
+{% codetabs %}
+{% ES5 %}
+```js
+var el = wp.element.createElement,
+	registerBlockType = wp.blocks.registerBlockType;
+
+registerBlockType( 'gutenberg-boilerplate-es5/hello-world-step-02', {
+	title: 'Hello World (Step 2)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	edit: function( props ) {
+		return el( 'p', { className: props.className }, 'Hello editor.' );
+	},
+
+	save: function( props ) {
+		return el( 'p', { className: props.className }, 'Hello saved content.' );
+	},
+} );
+```
+{% ESNext %}
+```js
+const { registerBlockType } = wp.blocks;
+
+registerBlockType( 'gutenberg-boilerplate-esnext/hello-world-step-02', {
+	title: 'Hello World (Step 2)',
+
+	icon: 'universal-access-alt',
+
+	category: 'layout',
+
+	edit( { className } ) {
+		return <p className={ className }>Hello editor.</p>;
+	},
+
+	save( { className } ) {
+		return <p className={ className }>Hello saved content.</p>;
+	},
+} );
+```
+{% end %}
+
+The class name is generated using the block's name prefixed with `wp-block-`, replacing the `/` namespace separator with a single `-`.
+
+```css
+.wp-block-gutenberg-boilerplate-es5-hello-world-step-02 {
+	color: green;
+	background: #cfc;
+	border: 2px solid #9c9;
+	padding: 20px;
+}
+```
+
+## Enqueuing Block Styles
+
+Like scripts, your block's editor-specific styles should be enqueued during the `enqueue_block_editor_assets` action.
+
+```php
+<?php
+
+function gutenberg_boilerplate_enqueue_block_editor_assets() {
+	wp_enqueue_script(
+		'gutenberg-boilerplate-es5-step02',
+		plugins_url( 'step-02/block.js', __FILE__ ),
+		array( 'wp-blocks', 'wp-element' )
+	);
+	wp_enqueue_style(
+		'gutenberg-boilerplate-es5-step02-editor',
+		plugins_url( 'step-02/editor.css', __FILE__ ),
+		array( 'wp-edit-blocks' ),
+		filemtime( plugin_dir_path( __FILE__ ) . 'step-02/editor.css' )
+	);
+}
+add_action( 'enqueue_block_editor_assets', 'gutenberg_boilerplate_enqueue_block_editor_assets' );
+```
+
+## Front-End Styling
+
+While a block's scripts are only necessary to load in the editor, you'll want to load styles both on the front of your site and in the editor. You may even want distinct styles in each context.
+
+The `enqueue_block_editor_assets` action will only be triggered when the editor is loading. To enqueue styles for your block when viewing the front of your site, you should do so during the `enqueue_block_assets` action.
+
+```php
+<?php
+
+function gutenberg_boilerplate_es5_enqueue_common_assets() {
+	wp_enqueue_style(
+		'gutenberg-boilerplate-es5-step02',
+		plugins_url( 'step-02/style.css', __FILE__ ),
+		array( 'wp-blocks' ),
+		filemtime( plugin_dir_path( __FILE__ ) . 'step-02/style.css' )
+	);
+}
+add_action( 'enqueue_block_assets', 'gutenberg_boilerplate_es5_enqueue_common_assets' );
+```
+
+The `enqueue_block_assets` action is triggered both in the editor and on the front of the site. Since your block is likely to share some styles in both contexts, you can consider `style.css` as the base stylesheet, placing editor-specific styles in `editor.css`.
diff --git a/docs/blocks.md b/docs/blocks.md
new file mode 100644
index 0000000000000..b601c645fa6a8
--- /dev/null
+++ b/docs/blocks.md
@@ -0,0 +1,7 @@
+# Blocks
+
+The purpose of this tutorial is to step through the fundamentals of creating a new block type. Beginning with the simplest possible example, each new section will incrementally build upon the last to include more of the common functionality you could expect to need when implementing your own block types.
+
+To follow along with this tutorial, you can [download the encompanying WordPress plugin](https://github.com/nylen/gutenberg-examples) which includes all of the examples for you to try on your own site. At each step along the way, you should feel free to experiment by modifying the examples with your own ideas and observing the effects they have on the block's behavior.
+
+Code snippets are provided both for "classic" JavaScript (ECMAScript 5, or "ES5"), as well as newer versions of the language standard (ES2015 and newer, or "ESNext"). You can change between them using tabs found above each code example. When choosing to author your blocks with ESNext, you will need a build step in order to support older browsers. Note that it is not required to use ESNext to create a new block, and you are welcome to use classic JavaScript if you so choose.
diff --git a/docs/coding-guidelines.md b/docs/coding-guidelines.md
index 8032bef4130d6..241b2fff2da2c 100644
--- a/docs/coding-guidelines.md
+++ b/docs/coding-guidelines.md
@@ -1,5 +1,4 @@
-Coding Guidelines
-=================
+# Coding Guidelines
 
 This living document serves to prescribe coding guidelines specific to the Gutenberg editor project. Base coding guidelines follow the [WordPress Coding Standards](https://codex.wordpress.org/WordPress_Coding_Standards). The following sections outline additional patterns and conventions used in the Gutenberg project.
 
diff --git a/docs/design.md b/docs/design.md
index 743aa0c1b4c17..0fbca1a00ab42 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -1,11 +1,10 @@
-Gutenberg Design
-================
+# Design Principles
 
 This living document serves to describe some of the design principles and patterns that have served to form the editor interface.
 
 ## Goal
 
-The all-encompassing goal of Gutenberg is to create a post and page building experience that makes it easy to create _rich post layouts_. From the <a href="https://make.wordpress.org/core/2017/01/04/focus-tech-and-design-leads/">kickoff post<a/>:
+The all-encompassing goal of Gutenberg is to create a post and page building experience that makes it easy to create _rich post layouts_. 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 it easy what today might take shortcodes, custom HTML, or “mystery meat” embed discovery.
 
diff --git a/docs/faq.md b/docs/faq.md
index 8b590470c348f..21c2115c243da 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,4 +1,4 @@
-# Gutenberg F.A.Q.
+# Frequently Asked Questions
 
 ## What is Gutenberg?
 
@@ -132,7 +132,7 @@ $blocks = $parser->parse( $post_content );
 Blocks are likely to become the main way users interact with content. Users are going to be discovering functionality in the new universal inserter tool, with a richer block interface that provides more layout opportunities.
 
 ## What features will be available at launch? What does the post-launch roadmap look like?
-As part of the focus on the editor in 2017, a focus on customization and sitebuilding is next. From <a href=”https://make.wordpress.org/core/2017/01/04/focus-tech-and-design-leads/”>the kickoff post</a>:
+As part of the focus on the editor in 2017, a focus on customization and sitebuilding is next. From [the kickoff post](https://make.wordpress.org/core/2017/01/04/focus-tech-and-design-leads/):
 
 > The customizer will help out the editor at first, then shift to bring those fundamental building blocks into something that could allow customization “outside of the box” of post_content, including sidebars and possibly even an entire theme.
 
diff --git a/docs/glossary.md b/docs/glossary.md
new file mode 100644
index 0000000000000..70a7fff8353ec
--- /dev/null
+++ b/docs/glossary.md
@@ -0,0 +1,15 @@
+# Glossary
+
+- __Attribute matchers__: An object describing the attributes shape of a block. The keys can be named as most appropriate to describe the state of a block type. The value for each key is a function which describes the strategy by which the attribute value should be extracted from the content of a saved post's content. When processed, a new object is created, taking the form of the keys defined in the attribute matchers, where each value is the result of the attribute matcher function.
+- __Attributes__: The object representation of the current state of a block in post content. When loading a saved post, this is determined by the attribute matchers for the block type. These values can change over time during an editing session when the user modifies a block, and are used when determining how to serialize the block.
+- __Block__: The abstract term used to describe units of markup that, composed together, form the content or layout of a webpage. The idea combines concepts of what in WordPress today we achieve with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.
+- __Block name__: A unique identifier for a block type, consisting of a plugin-specific namespace and a short label describing the block's intent. e.g. `core/image`
+- __Block type__: In contrast with the blocks composing a particular post, a block type describes the blueprint by which any block of that type should behave. So while there may be many images within a post, each behaves consistent with a unified image block type definition. 
+- __Dynamic block__: A type of block where the content of which may change and cannot be determined at the time of saving a post, instead calculated any time the post is shown on the front of a site. These blocks may save fallback content or no content at all in their JavaScript implementation, instead deferring to a PHP block implementation for runtime rendering.
+- __Editable__: A common component enabling rich content editing including bold, italics, hyperlinks, etc. It is not too much unlike the single editor region of the legacy post editor, and is in fact powered by the same TinyMCE library.
+- __Inspector__: A block settings region shown in place of the post settings when a block is selected. Fields may be shown here to allow the user to customize the selected block.
+- __Post settings__: A sidebar region containing metadata fields for the post, including scheduling, visibility, terms, and featured image.
+- __Serialization__: The process of converting a block's attributes object into HTML markup, typically occurring when saving the post. 
+- __Static block__: A type of block where the content of which is known at the time of saving a post. A static block will be saved with HTML markup directly in post content.
+- __TinyMCE__: [TinyMCE](https://www.tinymce.com/) is a web-based JavaScript WYSIWYG (What You See Is What You Get) editor.
+- __Toolbar__: A set of button controls. In the context of a block, usually referring to the toolbar of block controls shown above the selected block.
diff --git a/docs/index.js b/docs/index.js
new file mode 100644
index 0000000000000..cff85abc0e16d
--- /dev/null
+++ b/docs/index.js
@@ -0,0 +1,86 @@
+/**
+ * WordPress dependencies
+ */
+import { addStory } from 'docutron';
+
+addStory( {
+	name: 'intro',
+	title: 'Introduction',
+	path: '/',
+	markdown: require( './readme.md' ),
+} );
+
+addStory( {
+	name: 'blocks',
+	title: 'Creating Block Types',
+	markdown: require( './blocks.md' ),
+} );
+
+addStory( {
+	parents: [ 'blocks' ],
+	name: 'writing-your-first-block-type',
+	title: 'Writing Your First Block Type',
+	markdown: require( 'blocks-basic.md' ),
+} );
+
+addStory( {
+	parents: [ 'blocks' ],
+	name: 'applying-styles-with-stylesheets',
+	title: 'Applying Styles With Stylesheets',
+	markdown: require( './blocks-stylesheet.md' ),
+} );
+
+addStory( {
+	parents: [ 'blocks' ],
+	name: 'introducing-attributes-and-editable-fields',
+	title: 'Introducing Attributes and Editable Fields',
+	markdown: require( './blocks-editable.md' ),
+} );
+
+addStory( {
+	parents: [ 'blocks' ],
+	name: 'block-controls-toolbars-and-inspector',
+	title: 'Block Controls: Toolbars and Inspector',
+	markdown: require( './blocks-controls.md' ),
+} );
+
+addStory( {
+	name: 'reference',
+	title: 'Reference',
+	markdown: require( './reference.md' ),
+} );
+
+addStory( {
+	parents: [ 'reference' ],
+	name: 'attribute-matchers',
+	title: 'Attribute Matchers',
+	markdown: require( './attribute-matchers.md' ),
+} );
+
+addStory( {
+	parents: [ 'reference' ],
+	name: 'glossary',
+	title: 'Glossary',
+	markdown: require( './glossary.md' ),
+} );
+
+addStory( {
+	parents: [ 'reference' ],
+	name: 'design-principles',
+	title: 'Design Principles',
+	markdown: require( './design.md' ),
+} );
+
+addStory( {
+	parents: [ 'reference' ],
+	name: 'coding-guidelines',
+	title: 'Coding Guidelines',
+	markdown: require( './coding-guidelines.md' ),
+} );
+
+addStory( {
+	parents: [ 'reference' ],
+	name: 'faq',
+	title: 'Frequently Asked Questions',
+	markdown: require( './faq.md' ),
+} );
diff --git a/docs/readme.md b/docs/readme.md
new file mode 100644
index 0000000000000..55afe27762232
--- /dev/null
+++ b/docs/readme.md
@@ -0,0 +1,13 @@
+# Introduction
+
+"Gutenberg" is the codename for the 2017 WordPress editor focus. The goal if this focus is to create a new post and page editing experience that makes it easy for anyone to create rich post layouts. This was the kickoff goal:
+ 
+> The editor will endeavour to 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.
+ 
+Key take-aways from parsing that paragraph:
+ 
+- Authoring richly laid out posts is a key strength of WordPress.
+- By embracing "the block", we can potentially unify multiple different interfaces into one. Instead of learning how to write shortcodes, custom HTML, or paste URLs to embed, you should do with just learning the block, and all the pieces should fall in place.
+- "Mystery meat" refers to hidden features in software, features that you have to discover. WordPress already supports a large amount of blocks and 30+ embeds, so let's surface them.
+ 
+Gutenberg is being developed on [GitHub](https://github.com/WordPress/gutenberg), and you can try [an early beta version today from the plugin repository](https://wordpress.org/plugins/gutenberg/). Though keep in mind it's not fully functional, feature complete, or production ready.
diff --git a/docs/reference.md b/docs/reference.md
new file mode 100644
index 0000000000000..3313c417fcd69
--- /dev/null
+++ b/docs/reference.md
@@ -0,0 +1,7 @@
+# Reference
+
+- [Attribute Matchers](./reference/attribute-matchers)
+- [Glossary](./reference/glossary)
+- [Design Principles](./reference/design-principles)
+- [Coding Guidelines](./reference/coding-guidelines)
+- [Frequently Asked Questions](./reference/faq)
diff --git a/docutron/.eslintrc.json b/docutron/.eslintrc.json
new file mode 100644
index 0000000000000..05ca3cd3a5f8a
--- /dev/null
+++ b/docutron/.eslintrc.json
@@ -0,0 +1,10 @@
+{
+	"env": {
+		"worker": true
+	},
+	"settings": {
+		"react": {
+			"pragma": "React"
+		}
+	}
+}
diff --git a/docutron/.gitignore b/docutron/.gitignore
new file mode 100644
index 0000000000000..d30f40ef4422f
--- /dev/null
+++ b/docutron/.gitignore
@@ -0,0 +1,21 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/docutron/bin/build.js b/docutron/bin/build.js
new file mode 100644
index 0000000000000..617372abb1d65
--- /dev/null
+++ b/docutron/bin/build.js
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+
+/**
+ * External Dependencies
+ */
+const path = require( 'path' );
+
+/**
+ * Internal Dependencies
+ */
+const extendsConfig = require( './helpers/extend-webpack-config' );
+
+const usersCwd = process.cwd();
+const docsFolder = process.argv[ 2 ];
+
+// webpack.config.prod.js checks this.
+process.env.NODE_ENV = 'production';
+
+// Load and edit the create-react-app config.
+process.chdir( path.resolve( __dirname, '../' ) );
+const webpackConfig = require( 'react-scripts/config/webpack.config.prod' );
+extendsConfig( webpackConfig, usersCwd, docsFolder );
+
+// Run the build.
+require( 'react-scripts/scripts/build' );
diff --git a/docutron/bin/cli.js b/docutron/bin/cli.js
new file mode 100755
index 0000000000000..73b4dfc5e610f
--- /dev/null
+++ b/docutron/bin/cli.js
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+
+/**
+ * External Dependencies
+ */
+const path = require( 'path' );
+const childProcess = require( 'child_process' );
+
+const command = process.argv[ 2 ];
+const docsFolder = process.argv[ 3 ] || 'docs';
+
+if ( command === 'start' ) {
+	childProcess.execSync( 'node ' + path.resolve( __dirname, 'start.js ' + docsFolder ), { stdio: [ 0, 1, 2 ] } );
+} else if ( command === 'build' ) {
+	childProcess.execSync( 'node ' + path.resolve( __dirname, 'build.js ' + docsFolder ), { stdio: [ 0, 1, 2 ] } );
+	childProcess.execSync( 'cd ' + path.resolve( __dirname, '../' ) + ' && ./node_modules/.bin/react-snapshot', { stdio: [ 0, 1, 2 ] } );
+} else {
+	console.error( 'Unknown command ' + command ); // eslint-disable-line no-console
+	process.exit( 1 );
+}
diff --git a/docutron/bin/helpers/extend-webpack-config.js b/docutron/bin/helpers/extend-webpack-config.js
new file mode 100644
index 0000000000000..224ea575cf30e
--- /dev/null
+++ b/docutron/bin/helpers/extend-webpack-config.js
@@ -0,0 +1,36 @@
+/**
+ * External Dependencies
+ */
+const path = require( 'path' );
+
+module.exports = function( webpackConfig, usersCwd, docsFolder ) {
+	// Adding "docutron" alias
+	webpackConfig.resolve.alias.docutron = path.resolve( __dirname, '../../src/config/' );
+
+	// Loading the config folder
+	webpackConfig.resolve.alias.config = path.resolve( usersCwd, docsFolder );
+	webpackConfig.resolve.modules = webpackConfig.resolve.modules.concat( [ webpackConfig.resolve.alias.config ] );
+
+	// Using the user's node_modules
+	const usersNodeModules = path.resolve( usersCwd, 'node_modules' );
+	webpackConfig.resolve.modules = webpackConfig.resolve.modules.concat( [ usersNodeModules ] );
+
+	// Deleting CRA scoping
+	webpackConfig.resolve.plugins = [];
+	webpackConfig.module.rules.forEach( ( rule ) => {
+		if ( rule.include ) {
+			rule.include = [ rule.include, webpackConfig.resolve.alias.config ];
+		}
+	} );
+
+	// Adding the markdown loader and exclude if from the file loader
+	webpackConfig.module.rules.forEach( rule => {
+		if ( rule.loader === require.resolve( 'file-loader' ) ) {
+			rule.exclude.push( /\.md/ );
+		}
+	} );
+	webpackConfig.module.rules.push( {
+		test: /\.md/,
+		use: require.resolve( 'raw-loader' ),
+	} );
+};
diff --git a/docutron/bin/start.js b/docutron/bin/start.js
new file mode 100644
index 0000000000000..d72d5a27e4534
--- /dev/null
+++ b/docutron/bin/start.js
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+
+/**
+ * External Dependencies
+ */
+const path = require( 'path' );
+
+/**
+ * Internal Dependencies
+ */
+const extendConfig = require( './helpers/extend-webpack-config' );
+
+const usersCwd = process.cwd();
+const docsFolder = process.argv[ 2 ];
+
+// webpack.config.prod.js checks this.
+process.env.NODE_ENV = 'development';
+
+// Load and edit the create-react-app config.
+process.chdir( path.resolve( __dirname, '../' ) );
+const webpackConfig = require( 'react-scripts/config/webpack.config.dev' );
+extendConfig( webpackConfig, usersCwd, docsFolder );
+
+// Run the build.
+require( 'react-scripts/scripts/start' );
diff --git a/docutron/package.json b/docutron/package.json
new file mode 100644
index 0000000000000..028386ad1ef94
--- /dev/null
+++ b/docutron/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "docutron",
+  "version": "5000.0.0",
+  "dependencies": {
+    "lodash": "^4.17.4",
+    "markdown-it": "^8.3.1",
+    "markdown-it-prism": "^1.1.1",
+    "markdown-it-toc-and-anchor": "^4.1.2",
+    "prismjs": "^1.6.0",
+    "react": "^15.6.1",
+    "react-dom": "^15.6.1",
+    "react-router-dom": "^4.1.1",
+    "react-scripts": "1.0.10",
+    "react-snapshot": "^1.1.0",
+    "store": "^2.0.12"
+  },
+  "devDependencies": {},
+  "bin": {
+    "docutron": "./bin/cli.js"
+  }
+}
diff --git a/docutron/public/index.html b/docutron/public/index.html
new file mode 100644
index 0000000000000..4a04d5ece7745
--- /dev/null
+++ b/docutron/public/index.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+		<meta name="theme-color" content="#000000">
+		<!--
+			manifest.json provides metadata used when your web app is added to the
+			homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
+		-->
+		<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
+		<link rel="stylesheet" type="text/css" href="https://s.w.org/style/wp4.css?58">
+		<link rel="stylesheet" type="text/css" href="https://make.wordpress.org/docs/wp-content/themes/p2-breathe/style.css">
+		<link rel="stylesheet" type="text/css" href="https://make.wordpress.org/docs/wp-content/themes/pub/wporg-breathe/style.css">
+		<style>h1 .markdownIt-Anchor { display: none; }</style>
+		<!--
+			Notice the use of %PUBLIC_URL% in the tags above.
+			It will be replaced with the URL of the `public` folder during the build.
+			Only files inside the `public` folder can be referenced from the HTML.
+
+			Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
+			work correctly both with client-side routing and a non-root public URL.
+			Learn how to configure a non-root public URL by running `npm run build`.
+		-->
+		<title>Gutenberg Docs</title>
+	</head>
+	<body>
+		<noscript>
+			You need to enable JavaScript to run this app.
+		</noscript>
+		<div id="root"></div>
+		<!--
+			This HTML file is a template.
+			If you open it directly in the browser, you will see an empty page.
+
+			You can add webfonts, meta tags, or analytics to this file.
+			The build step will place the bundled scripts into the <body> tag.
+
+			To begin the development, run `npm start` or `yarn start`.
+			To create a production bundle, use `npm run build` or `yarn build`.
+		-->
+	</body>
+</html>
diff --git a/docutron/public/manifest.json b/docutron/public/manifest.json
new file mode 100644
index 0000000000000..588bc83707e27
--- /dev/null
+++ b/docutron/public/manifest.json
@@ -0,0 +1,8 @@
+{
+	"short_name": "Gutenberg Docs",
+	"name": "Gutenberg Docs",
+	"start_url": "./index.html",
+	"display": "standalone",
+	"theme_color": "#000000",
+	"background_color": "#ffffff"
+}
diff --git a/docutron/src/components/app.js b/docutron/src/components/app.js
new file mode 100644
index 0000000000000..aefc98d54d210
--- /dev/null
+++ b/docutron/src/components/app.js
@@ -0,0 +1,37 @@
+/**
+ * External Dependencies
+ */
+import React from 'react';
+import { BrowserRouter, Route } from 'react-router-dom';
+
+/**
+ * Internal Dependencies
+ */
+import { getStories } from '../config';
+import Header from './header';
+import Sidebar from './sidebar';
+import Page from './page';
+
+function App() {
+	return (
+		<BrowserRouter>
+			<div className="single-handbook">
+				<Header />
+				<div id="page">
+					<div id="main" className="site-main clear">
+						<Sidebar />
+						<div id="primary" className="content-area">
+							{ getStories().map( ( story, index ) => (
+								<Route key={ index } path={ story.path } exact render={
+									() => <Page story={ story } />
+								} />
+							) ) }
+						</div>
+					</div>
+				</div>
+			</div>
+		</BrowserRouter>
+	);
+}
+
+export default App;
diff --git a/docutron/src/components/header.js b/docutron/src/components/header.js
new file mode 100644
index 0000000000000..1cd20c2093059
--- /dev/null
+++ b/docutron/src/components/header.js
@@ -0,0 +1,43 @@
+/**
+ * External Dependencies
+ */
+import React from 'react';
+
+const header = (
+	<header id="masthead" className="site-header" role="banner">
+		<div className="site-branding">
+			<p className="site-title">
+				<a href="/" rel="home">
+					Gutenberg Editor
+				</a>
+			</p>
+		</div>
+		<nav id="site-navigation" className="navigation-main clear">
+			<div className="menu-navigation-container">
+				<ul id="menu-navigation" className="menu">
+					<li className="menu-item">
+						<a title="View on GitHub" href="https://github.com/WordPress/gutenberg/">
+							<span className="screen-reader-text">View on GitHub</span>
+							<svg
+								viewBox="0 0 16 16"
+								xmlns="http://www.w3.org/2000/svg"
+								fillRule="evenodd"
+								clipRule="evenodd"
+								strokeLinejoin="round"
+								strokeMiterlimit="1.414"
+								width="20"
+								height="20"
+								fill="#fff"
+								style={ { verticalAlign: 'middle' } }
+							>
+								<path d="M8 0C3.58 0 0 3.582 0 8c0 3.535 2.292 6.533 5.47 7.59.4.075.547-.172.547-.385 0-.19-.007-.693-.01-1.36-2.226.483-2.695-1.073-2.695-1.073-.364-.924-.89-1.17-.89-1.17-.725-.496.056-.486.056-.486.803.056 1.225.824 1.225.824.714 1.223 1.873.87 2.33.665.072-.517.278-.87.507-1.07-1.777-.2-3.644-.888-3.644-3.953 0-.873.31-1.587.823-2.147-.09-.202-.36-1.015.07-2.117 0 0 .67-.215 2.2.82.64-.178 1.32-.266 2-.27.68.004 1.36.092 2 .27 1.52-1.035 2.19-.82 2.19-.82.43 1.102.16 1.915.08 2.117.51.56.82 1.274.82 2.147 0 3.073-1.87 3.75-3.65 3.947.28.24.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.14.46.55.38C13.71 14.53 16 11.53 16 8c0-4.418-3.582-8-8-8" />
+							</svg>
+						</a>
+					</li>
+				</ul>
+			</div>
+		</nav>
+	</header>
+);
+
+export default () => header;
diff --git a/docutron/src/components/markdown-content.js b/docutron/src/components/markdown-content.js
new file mode 100644
index 0000000000000..06c2bc9672449
--- /dev/null
+++ b/docutron/src/components/markdown-content.js
@@ -0,0 +1,31 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+
+/**
+ * Internal dependencies
+ */
+import markdown from '../markdown';
+import Tabs from './tabs';
+
+function MarkdownContent( { content } ) {
+	const blocks = markdown( content );
+
+	return (
+		<div>
+			{ blocks.map( ( block, index ) => {
+				if ( block.type === 'raw' ) {
+					return <div key={ index } dangerouslySetInnerHTML={ { __html: block.content } } />;
+				}
+				if ( block.type === 'codetabs' ) {
+					return <Tabs key={ index } tabs={ block.tabs } />;
+				}
+
+				return null;
+			} ) }
+		</div>
+	);
+}
+
+export default MarkdownContent;
diff --git a/docutron/src/components/menu-item.js b/docutron/src/components/menu-item.js
new file mode 100644
index 0000000000000..2eec6d413ef7e
--- /dev/null
+++ b/docutron/src/components/menu-item.js
@@ -0,0 +1,44 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { find } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import { getChildren } from '../config';
+
+function MenuItem( { item, searchResults } ) {
+	const children = getChildren( item.id );
+	const isVisible = ! searchResults ||
+		!! find( searchResults, ( story ) => {
+			return story.id === item.id ||
+				( story.parents && story.parents.indexOf( item.id ) !== -1 );
+		} );
+
+	if ( ! isVisible ) {
+		return null;
+	}
+
+	return (
+		<li>
+			{ ! children.length && <Link to={ item.path }>{ item.title }</Link> }
+			{ !! children.length && (
+				<div className="expandable">
+					<Link to={ item.path }>{ item.title }</Link>
+				</div>
+			) }
+			{ !! children.length && (
+				<ul>
+					{ children.map( ( story, index ) => (
+						<MenuItem key={ index } item={ story } searchResults={ searchResults } />
+					) ) }
+				</ul>
+			) }
+		</li>
+	);
+}
+
+export default MenuItem;
diff --git a/docutron/src/components/page.js b/docutron/src/components/page.js
new file mode 100644
index 0000000000000..7d53ee93c729f
--- /dev/null
+++ b/docutron/src/components/page.js
@@ -0,0 +1,49 @@
+/**
+ * External Dependencies
+ */
+import React, { Component } from 'react';
+import { Link } from 'react-router-dom';
+
+/**
+ * Internal Dependencies
+ */
+import MarkdownContent from './markdown-content';
+import { getNextStory, getPreviousStory } from '../config';
+
+class Page extends Component {
+	componentDidMount() {
+		window.scrollTo( 0, 0 );
+	}
+
+	render() {
+		const { story } = this.props;
+		const nextStory = getNextStory( story.id );
+		const previousStory = getPreviousStory( story.id );
+
+		return (
+			<div>
+				{ !! story.Component && <story.Component /> }
+				{ !! story.markdown && <MarkdownContent content={ story.markdown } /> }
+
+				<div className="navigation">
+					{ !! previousStory && (
+						<p className="nav-older">
+							<Link to={ previousStory.path } rel="previous">
+								← { previousStory.title }
+							</Link>
+						</p>
+					) }
+					{ !! nextStory && (
+						<p className="nav-newer">
+							<Link to={ nextStory.path } rel="next">
+								{ nextStory.title } →
+							</Link>
+						</p>
+					) }
+				</div>
+			</div>
+		);
+	}
+}
+
+export default Page;
diff --git a/docutron/src/components/sidebar.js b/docutron/src/components/sidebar.js
new file mode 100644
index 0000000000000..396dd9040379a
--- /dev/null
+++ b/docutron/src/components/sidebar.js
@@ -0,0 +1,73 @@
+/**
+ * External Dependencies
+ */
+import React, { Component } from 'react';
+
+/**
+ * Internal Dependencies
+ */
+import MenuItem from './menu-item';
+import { getStories } from '../config';
+
+class Sidebar extends Component {
+	constructor() {
+		super( ...arguments );
+		this.state = {
+			searchValue: '',
+		};
+		this.onChangeSearchValue = this.onChangeSearchValue.bind( this );
+	}
+
+	onChangeSearchValue( event ) {
+		this.setState( { searchValue: event.target.value } );
+	}
+
+	render() {
+		const searchResults = this.state.searchValue
+			? getStories()
+				.filter( ( story ) => story.title.toLowerCase().indexOf( this.state.searchValue.toLowerCase() ) !== -1 )
+			: null;
+
+		return (
+			<div id="secondary" className="widget-area">
+				<div className="secondary-content">
+					<aside className="widget widget_search">
+						<form
+							id="searchform"
+							className="searchform"
+							role="search"
+							onSubmit={ ( event ) => event.preventDefault() }>
+							<label htmlFor="s" className="screen-reader-text">Filter</label>
+							<input
+								type="search"
+								className="field"
+								name="s"
+								value={ this.state.searchValue }
+								id="s"
+								placeholder="Filter …"
+								onChange={ this.onChangeSearchValue }
+							/>
+							<input type="submit" className="submit" id="searchsubmit" value="Filter" />
+						</form>
+					</aside>
+
+					<aside id="handbook" className="widget widget_wporg_handbook_pages">
+						<h2 className="widget-title">Chapters</h2>
+						<div className="menu-table-of-contents-container">
+							<ul>
+								{ getStories()
+									.filter( ( story ) => ! story.parent )
+										.map( ( story, index ) => (
+											<MenuItem key={ index } item={ story } searchResults={ searchResults } />
+										) )
+								}
+							</ul>
+						</div>
+					</aside>
+				</div>
+			</div>
+		);
+	}
+}
+
+export default Sidebar;
diff --git a/docutron/src/components/tabs.js b/docutron/src/components/tabs.js
new file mode 100644
index 0000000000000..c92811d561ec0
--- /dev/null
+++ b/docutron/src/components/tabs.js
@@ -0,0 +1,71 @@
+/**
+ * External Dependencies
+ */
+import React, { PureComponent } from 'react';
+import store from 'store';
+import { EventEmitter } from 'events';
+
+// Create a shared event emitter so that changes to a preference from one code
+// snippet are reflected immediately in all other mounted Tabs components.
+const bus = new EventEmitter();
+
+class Tabs extends PureComponent {
+	constructor( props ) {
+		super( ...arguments );
+
+		this.onPreferenceChange = this.onPreferenceChange.bind( this );
+
+		const key = this.getPreferenceKey( props.tabs );
+		this.state = {
+			activeTab: Number( store.get( key ) ) || 0,
+		};
+	}
+
+	componentDidMount() {
+		bus.on( 'change', this.onPreferenceChange );
+	}
+
+	componentWillUnmount() {
+		bus.removeListener( 'change', this.onPreferenceChange );
+	}
+
+	getPreferenceKey( tabs ) {
+		return 'tabs-preference-' + tabs.map( ( tab ) => tab.name ).join();
+	}
+
+	onPreferenceChange( key, index ) {
+		if ( key === this.getPreferenceKey( this.props.tabs ) ) {
+			this.setState( { activeTab: index } );
+		}
+	}
+
+	selectTab( index ) {
+		return () => {
+			const key = this.getPreferenceKey( this.props.tabs );
+			store.set( key, index );
+			bus.emit( 'change', key, index );
+		};
+	}
+
+	render() {
+		const { tabs } = this.props;
+		const activeTab = tabs[ this.state.activeTab ];
+
+		return (
+			<div>
+				{ tabs.map( ( tab, index ) => (
+					<button
+						key={ index }
+						onClick={ this.selectTab( index ) }
+						className={ index === this.state.activeTab ? 'components-code-tab is-active' : 'components-code-tab' }
+					>
+						{ tab.name }
+					</button>
+				) ) }
+				{ activeTab && <div dangerouslySetInnerHTML={ { __html: activeTab.content } } /> }
+			</div>
+		);
+	}
+}
+
+export default Tabs;
diff --git a/docutron/src/config/index.js b/docutron/src/config/index.js
new file mode 100644
index 0000000000000..49f3975f9774f
--- /dev/null
+++ b/docutron/src/config/index.js
@@ -0,0 +1,49 @@
+/**
+ * External Dependencies
+ */
+import { find } from 'lodash';
+
+const stories = [];
+
+export function addStory( story ) {
+	const { name, parents = [] } = story;
+	stories.push( {
+		path: '/' + parents.concat( name ).join( '/' ) + '/',
+		id: parents.concat( name ).join( '.' ),
+		parent: parents.join( '.' ),
+		...story,
+	} );
+}
+
+export function getStories() {
+	return stories;
+}
+
+export function getStory( id ) {
+	return find( stories, ( story ) => story.id === id );
+}
+
+export function getChildren( id ) {
+	return stories.filter( ( story ) => story.parent === id );
+}
+
+export function getOrderedPageList( parentId = '' ) {
+	return getChildren( parentId ).reduce( ( memo, story ) => {
+		memo.push( story );
+		return memo.concat( getOrderedPageList( story.id ) );
+	}, [] );
+}
+
+export function getNextStory( id ) {
+	const orderedList = getOrderedPageList();
+	const index = orderedList.indexOf( getStory( id ) );
+
+	return orderedList[ index + 1 ];
+}
+
+export function getPreviousStory( id ) {
+	const orderedList = getOrderedPageList();
+	const index = orderedList.indexOf( getStory( id ) );
+
+	return orderedList[ index - 1 ];
+}
diff --git a/docutron/src/index.js b/docutron/src/index.js
new file mode 100644
index 0000000000000..6051f571d6a71
--- /dev/null
+++ b/docutron/src/index.js
@@ -0,0 +1,19 @@
+/**
+ * External Dependencies
+ */
+import React from 'react';
+import { render } from 'react-snapshot';
+import 'prismjs/themes/prism.css';
+
+/**
+ * User Dependencies
+ */
+import 'config';
+
+/**
+ * Internal Dependencies
+ */
+import App from './components/app';
+import './styles/main.css';
+
+render( <App />, document.getElementById( 'root' ) );
diff --git a/docutron/src/markdown/index.js b/docutron/src/markdown/index.js
new file mode 100644
index 0000000000000..0aa15b08c1fc6
--- /dev/null
+++ b/docutron/src/markdown/index.js
@@ -0,0 +1,61 @@
+/**
+ * External Dependencies
+ */
+import MarkdownIt from 'markdown-it';
+import markdownItPrismPlugin from 'markdown-it-prism';
+import markdownItTOCAndAnchorPlugin from 'markdown-it-toc-and-anchor';
+import { compact } from 'lodash';
+
+const parser = new MarkdownIt( {
+	html: true,
+} );
+parser
+	.use( markdownItTOCAndAnchorPlugin )
+	.use( markdownItPrismPlugin );
+
+const blockParsers = {
+	raw( content ) {
+		return {
+			type: 'raw',
+			content: parser.render( content ),
+		};
+	},
+
+	codetabs( content ) {
+		const tabsRegex = /{%\s+([\w]+)\s+%}/gm;
+		const splittedTabs = compact( content.trim().split( tabsRegex ) );
+		const tabs = [];
+		for ( let i = 0; i < splittedTabs.length; i = i + 2 ) {
+			tabs.push( {
+				name: splittedTabs[ i ],
+				content: parser.render( splittedTabs[ i + 1 ] ),
+			} );
+		}
+
+		return {
+			type: 'codetabs',
+			tabs,
+		};
+	},
+};
+
+function parse( markdown ) {
+	const blocksRegex = /({%\s+[\w]+\s+%}(?:.|\n|\r)*?{%\s+end\s+%})/gm;
+	const blockRegex = /{%\s+([\w]+)\s+%}((?:.|\n|\r)*?){%\s+end\s+%}/gm;
+	const blocks = markdown.split( blocksRegex );
+	return blocks
+		.map( ( block ) => {
+			const matches = blockRegex.exec( block );
+			if ( ! matches ) {
+				return { type: 'raw', content: block };
+			}
+			return { type: matches[ 1 ], content: matches[ 2 ] };
+		} )
+		.map( ( block ) => {
+			const blockParser = blockParsers[ block.type ] || blockParsers.raw;
+
+			return blockParser( block.content );
+		} );
+}
+
+export default parse;
diff --git a/docutron/src/styles/main.css b/docutron/src/styles/main.css
new file mode 100644
index 0000000000000..1cf3e4eca3ab7
--- /dev/null
+++ b/docutron/src/styles/main.css
@@ -0,0 +1,29 @@
+body {
+	margin: 0;
+}
+
+#secondary {
+	padding: 4rem 0 0;
+}
+
+code {
+	tab-size: 4;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+	background-color: #f7f7f7 !important;
+}
+
+.components-code-tab {
+	border-radius: 0;
+}
+
+.components-code-tab.is-active {
+	border-color: #cccccc;
+	box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.5), inset 0 2px 5px rgba(0, 0, 0, 0.15);
+}
+
+.widget_search {
+	background: transparent;
+}
diff --git a/editor/story/index.js b/editor/story/index.js
deleted file mode 100644
index 08cbfea30a119..0000000000000
--- a/editor/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Modules', module )
-	.addDecorator( withKnobs )
-	.add( 'Editor', () => <ReactMarkdown source={ readme } /> );
diff --git a/element/story/index.js b/element/story/index.js
deleted file mode 100644
index 31ec383f04352..0000000000000
--- a/element/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Modules', module )
-	.addDecorator( withKnobs )
-	.add( 'Element', () => <ReactMarkdown source={ readme } /> );
diff --git a/i18n/story/index.js b/i18n/story/index.js
deleted file mode 100644
index 459d414852175..0000000000000
--- a/i18n/story/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * External dependencies
- */
-import ReactMarkdown from 'react-markdown';
-import { storiesOf } from '@storybook/react';
-import { withKnobs } from '@storybook/addon-knobs';
-
-/**
- * Internal dependencies
- */
-import readme from '../README.md';
-
-storiesOf( 'Modules', module )
-	.addDecorator( withKnobs )
-	.add( 'i18n', () => <ReactMarkdown source={ readme } /> );
diff --git a/package.json b/package.json
index bd6201cf641c7..efbac118e9c53 100644
--- a/package.json
+++ b/package.json
@@ -37,10 +37,6 @@
     "uuid": "^3.0.1"
   },
   "devDependencies": {
-    "@storybook/addon-info": "^3.0.1",
-    "@storybook/addon-knobs": "^3.0.1",
-    "@storybook/addon-options": "^3.0.1",
-    "@storybook/react": "^3.0.0",
     "autoprefixer": "^6.7.7",
     "babel-core": "^6.24.0",
     "babel-eslint": "^7.2.0",
@@ -130,13 +126,13 @@
   "scripts": {
     "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
     "gettext-strings": "cross-env BABEL_ENV=gettext webpack",
-    "lint": "eslint . .storybook",
+    "lint": "eslint .",
     "dev": "cross-env BABEL_ENV=default webpack --watch",
     "test": "npm run lint && npm run test-unit",
     "ci": "concurrently \"npm run lint && npm run build\" \"npm run test-unit:coverage-ci\"",
     "package-plugin": "./bin/build-plugin-zip.sh",
-    "storybook": "start-storybook -p 6006",
-    "build-storybook": "build-storybook",
+    "docs-start": "./docutron/bin/cli.js start",
+    "docs-build": "./docutron/bin/cli.js build",
     "test-unit": "jest",
     "test-unit:coverage": "jest --coverage",
     "test-unit:coverage-ci": "jest --coverage --maxWorkers 1 && coveralls < coverage/lcov.info",