diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..86d1c18cbc716e8fc5ce61a8e4e1aabaafbaa075
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,4 @@
+
+content.md: content.json gen_content.py
+	./gen_content.py > content.md
+
diff --git a/docs/content.json b/docs/content.json
new file mode 100644
index 0000000000000000000000000000000000000000..bbf7a863c6cf8b031e4b8e8289f3e96adef8a565
--- /dev/null
+++ b/docs/content.json
@@ -0,0 +1,151 @@
+{
+  "header": {
+    "eclass": "block",
+    "level": "int",
+    "attr": "attr",
+    "content": "inlines"
+  },
+  "rawblock": {
+    "eclass": "block",
+    "format": "text",
+    "raw": "text"
+  },
+  "bulletlist": {
+    "eclass": "block",
+    "items": "[blocks]",
+    "count": "int"
+  },
+  "orderedlist": {
+    "eclass": "block",
+    "items": "[blocks]",
+    "count": "int",
+    "start": "int",
+    "style": [
+      "default",
+      "example",
+      "decimal",
+      "lower_roman",
+      "upper_roman",
+      "lower_alpha",
+      "upper_alpha"
+    ],
+    "delim": [
+      "default",
+      "period",
+      "one_parenthesis",
+      "two_parentheses"
+    ]
+  },
+  "blockquote": {
+    "eclass": "block",
+    "content": "blocks"
+  },
+  "plain": {
+    "eclass": "block",
+    "content": "inlines"
+  },
+  "paragraph": {
+    "eclass": "block",
+    "content": "inlines"
+  },
+  "codeblock": {
+    "eclass": "block",
+    "attr": "attr",
+    "code": "text",
+    "code_lines": "[text]"
+  },
+  "horizontalrule": {
+    "eclass": "block"
+  },
+  "blockcontainer": {
+    "eclass": "block",
+    "attr": "attr",
+    "content": "blocks"
+  },
+  "space": {
+    "eclass": "inline"
+},
+  "linebreak": {
+    "eclass": "inline"
+},
+  "softbreak": {
+    "eclass": "inline"
+},
+  "string": {
+    "eclass": "inline",
+    "text": "text"
+  },
+  "strong": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "emph": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "underline": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "strikeout": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "superscript": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "subscript": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "smallcaps": {
+    "eclass": "inline",
+    "content": "inlines"
+  },
+  "link": {
+    "eclass": "inline",
+    "attr": "attr",
+    "content": "inlines",
+    "url": "text",
+    "title": "text"
+  },
+  "image": {
+    "eclass": "inline",
+    "attr": "attr",
+    "alt": "inlines",
+    "url": "text",
+    "title": "text"
+  },
+  "quoted": {
+    "eclass": "inline",
+    "quotetype": [
+      "single",
+      "double"
+    ],
+    "text": "text"
+  },
+  "math": {
+    "eclass": "inline",
+    "mathtype": [
+      "display",
+      "inline"
+    ],
+    "math": "text"
+  },
+  "code": {
+    "eclass": "inline",
+    "attr": "attr",
+    "code": "text",
+    "code_lines": "[text]"
+  },
+  "inlinecontainer": {
+    "eclass": "inline",
+    "format": "text",
+    "raw": "text"
+  },
+  "footnote": {
+    "eclass": "inline",
+    "content": "blocks"
+  }
+}
diff --git a/docs/content.md b/docs/content.md
new file mode 100644
index 0000000000000000000000000000000000000000..e8119bb47df7fa60cf744317dab45b01a70113ae
--- /dev/null
+++ b/docs/content.md
@@ -0,0 +1,292 @@
+
+# Content Format
+
+
+
+
+
+
+## Attr
+
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| id | String |  |
+| classes | \[String\] |  |
+| extra | {foo: bar, alice: bob, ...} |  |
+
+
+## Blocks
+
+
+### header
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `header` |  |
+| eclass | `block` |  |
+| level | Integer |  |
+| attr | [Attr](#attr) |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### rawblock
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `rawblock` |  |
+| eclass | `block` |  |
+| format | String |  |
+| raw | String |  |
+
+
+### bulletlist
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `bulletlist` |  |
+| eclass | `block` |  |
+| items | \[\[[Block](#blocks)\]\] |  |
+| count | Integer |  |
+
+
+### orderedlist
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `orderedlist` |  |
+| eclass | `block` |  |
+| items | \[\[[Block](#blocks)\]\] |  |
+| count | Integer |  |
+| start | Integer |  |
+| style | `default` \| `example` \| `decimal` \| `lower_roman` \| `upper_roman` \| `lower_alpha` \| `upper_alpha` |  |
+| delim | `default` \| `period` \| `one_parenthesis` \| `two_parentheses` |  |
+
+
+### blockquote
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `blockquote` |  |
+| eclass | `block` |  |
+| content | \[[Block](#blocks)\] |  |
+
+
+### plain
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `plain` |  |
+| eclass | `block` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### paragraph
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `paragraph` |  |
+| eclass | `block` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### codeblock
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `codeblock` |  |
+| eclass | `block` |  |
+| attr | [Attr](#attr) |  |
+| code | String |  |
+| code_lines | \[String\] |  |
+
+
+### horizontalrule
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `horizontalrule` |  |
+| eclass | `block` |  |
+
+
+### blockcontainer
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `blockcontainer` |  |
+| eclass | `block` |  |
+| attr | [Attr](#attr) |  |
+| content | \[[Block](#blocks)\] |  |
+
+
+## Inlines
+
+
+### space
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `space` |  |
+| eclass | `inline` |  |
+
+
+### linebreak
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `linebreak` |  |
+| eclass | `inline` |  |
+
+
+### softbreak
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `softbreak` |  |
+| eclass | `inline` |  |
+
+
+### string
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `string` |  |
+| eclass | `inline` |  |
+| text | String |  |
+
+
+### strong
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `strong` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### emph
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `emph` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### underline
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `underline` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### strikeout
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `strikeout` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### superscript
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `superscript` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### subscript
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `subscript` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### smallcaps
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `smallcaps` |  |
+| eclass | `inline` |  |
+| content | \[[Inline](#inlines)\] |  |
+
+
+### link
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `link` |  |
+| eclass | `inline` |  |
+| attr | [Attr](#attr) |  |
+| content | \[[Inline](#inlines)\] |  |
+| url | String |  |
+| title | String |  |
+
+
+### image
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `image` |  |
+| eclass | `inline` |  |
+| attr | [Attr](#attr) |  |
+| alt | \[[Inline](#inlines)\] |  |
+| url | String |  |
+| title | String |  |
+
+
+### quoted
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `quoted` |  |
+| eclass | `inline` |  |
+| quotetype | `single` \| `double` |  |
+| text | String |  |
+
+
+### math
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `math` |  |
+| eclass | `inline` |  |
+| mathtype | `display` \| `inline` |  |
+| math | String |  |
+
+
+### code
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `code` |  |
+| eclass | `inline` |  |
+| attr | [Attr](#attr) |  |
+| code | String |  |
+| code_lines | \[String\] |  |
+
+
+### inlinecontainer
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `inlinecontainer` |  |
+| eclass | `inline` |  |
+| format | String |  |
+| raw | String |  |
+
+
+### footnote
+
+| Key | Parser | Comment |
+|-----|--------|---------|
+| etype | `footnote` |  |
+| eclass | `inline` |  |
+| content | \[[Block](#blocks)\] |  |
diff --git a/docs/gen_content.py b/docs/gen_content.py
new file mode 100755
index 0000000000000000000000000000000000000000..6cb27e3262cdbb7d12b5342a5b445e6f87d4e5b1
--- /dev/null
+++ b/docs/gen_content.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+
+import json
+import os
+
+header = """
+# Content Format
+
+
+
+"""
+
+def print_entry(key, parser_raw):
+    # | key | parser | comment |
+    parser = '`' + str(parser_raw) + '`'
+
+    if parser_raw == "text":
+        parser = "String"
+    elif parser_raw == "int":
+        parser = "Integer"
+    elif parser_raw == "[text]":
+        parser = "\[String\]"
+    elif parser_raw == "attr":
+        parser = "[Attr](#attr)"
+    elif parser_raw == "blocks":
+        parser = "\[[Block](#blocks)\]"
+    elif parser_raw == "[blocks]":
+        parser = "\[\[[Block](#blocks)\]\]"
+    elif parser_raw == "inlines":
+        parser = "\[[Inline](#inlines)\]"
+    elif parser_raw == "kvps":
+        parser = "{foo: bar, alice: bob, ...}"
+    elif isinstance(parser_raw,list):
+        parser = '`' + '` \| `'.join(parser_raw) + '`'
+
+    print("| {key} | {parser} | {comment} |".format(key=key, parser=parser, comment=""))
+
+def print_table_header():
+    print("| Key | Parser | Comment |")
+    print("|-----|--------|---------|")
+
+
+def print_etype(etype, c):
+    print("")
+    print("")
+    print("### {etype}".format(etype=etype))
+    print("")
+    print_table_header()
+    print_entry("etype", etype)
+    for key, value in c.items():
+        print_entry(key, value)
+
+
+if __name__ == '__main__':
+    j = {}
+    with open('content.json') as f:
+        j = json.loads(f.read())
+
+    print(header)
+    print("")
+    print("")
+    print("## Attr")
+    print("")
+    print("")
+    print_table_header()
+    print_entry("id", "text")
+    print_entry("classes", "[text]")
+    print_entry("extra", "kvps")
+
+
+    print("")
+    print("")
+    print("## Blocks")
+    is_blocks = True
+    for etype, c in j.items():
+        if c['eclass'] != 'block' and is_blocks:
+            is_blocks = False
+            print("")
+            print("")
+            print("## Inlines")
+        print_etype(etype, c)
+
+