diff --git a/docs/templates.md b/docs/templates.md
new file mode 100644
index 00000000..1c9081d2
--- /dev/null
+++ b/docs/templates.md
@@ -0,0 +1,577 @@
+# Templates
+
+## Helpers
+
+Yomichan supports several custom Handlebars helpers for rendering templates.
+The source code for these templates can be found [here](../ext/bg/js/template-renderer.js).
+
+
+### `dumpObject`
+
+Converts an object to a pretty-printed JSON string.
+This function can be helpful for debugging values when creating templates.
+
+
+ Syntax:
+
+ {{#dumpObject}}<object>{{/dumpObject}}
+
+ * _`object`_
+ The object to convert.
+
+
+ Example:
+
+ ```handlebars
+ {{#dumpObject}}{{.}}{{/dumpObject}}
+ ```
+
+ Output:
+ ```html
+ {
+ "key": "value"
+ }
+ ```
+
+ Preview:
+ ```html
+ {
+ "key": "value"
+ }
+ ```
+
+
+
+### `furigana`
+
+Converts a definition to its furigana representation.
+
+
+ Syntax:
+
+ {{#furigana}}<definition>{{/furigana}}
+
+ * _`definition`_
+ The definition to convert.
+
+
+ Example:
+
+ ```handlebars
+ {{#furigana}}{{.}}{{/furigana}}
+ ```
+
+ Output:
+ ```html
+ 読む
+ ```
+
+ Preview
+ 読む
+
+
+
+### `furiganaPlain`
+
+Converts a definition to its simplified furigana representation.
+
+
+ Syntax:
+
+ {{#furiganaPlain}}<definition>{{/furigana}}
+
+ * _`definition`_
+ The definition to convert.
+
+
+ Example:
+
+ ```handlebars
+ {{~#furiganaPlain~}}{{.}}{{~/furiganaPlain~}}
+ ```
+
+ Output:
+ ```html
+ 読[よ]む
+ ```
+
+
+
+### `multiLine`
+
+Replaces newline characters with a forced HTML line break `
`.
+
+
+ Syntax:
+
+ {{#multiLine}}text with multiple lines{{/multiLine}}
+
+
+ Example:
+
+ ```handlebars
+ {{#kanjiLinks~}}
+ some
+ multiline
+ text
+ {{~/kanjiLinks}}
+ ```
+
+ Output:
+ ```html
+ some
multiline
text
+ ```
+
+ Preview:
+ some
multiline
text
+
+
+
+### `regexReplace`
+
+Uses a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) to replace a pattern with the specified text.
+
+
+ Syntax:
+
+ {{#regexReplace regex replacement [flags]}}text-to-modify{{/regexReplace}}
+
+ * _`regex`_
+ The raw string used to create the regular expression. This value is passed to the [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) constructor.
+ * _`replacement`_
+ The text used to replace pattern matches. This supports the standard [special capture group replacements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter) as supported by the web browser.
+ * _`flags`_ _(optional)_
+ Optional flags to pass to the [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) constructor.
+ * _`text-to-modify`_
+ The text that the regular expression is applied to.
+
+
+ Example:
+
+ ```handlebars
+ {{#regexReplace "\(([^)]*)\)" "$1" "g"~}}Here is (some) (text) (in) (parentheses){{~/regexReplace}}
+ ```
+
+ Output:
+ ```html
+ Here is some text in parentheses
+ ```
+
+
+
+### `regexMatch`
+
+Uses a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) to return only the content that matches the pattern.
+
+
+ Syntax:
+
+ {{#regexMatch regex [flags]}}text-to-modify{{/regexMatch}}
+
+ * _`regex`_
+ The raw string used to create the regular expression. This value is passed to the [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) constructor.
+ * _`flags`_ _(optional)_
+ Optional flags to pass to the [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) constructor.
+ * _`text-to-modify`_
+ The text that the regular expression is applied to.
+
+
+ Example:
+
+ ```handlebars
+ {{#regexMatch "\(([^)]*)\)" "g"~}}Here is (some) (text) (in) (parentheses){{~/regexMatch}}
+ ```
+
+ Output:
+ ```html
+ (some)(text)(in)(parentheses)
+ ```
+
+
+
+### `mergeTags`
+
+Creates a set of all unique tags for the definition and returns a text representation of the tags separated by commas.
+
+
+ Syntax:
+
+ {{#mergeTags definition isGroupMode isMergeMode}}{{/mergeTags}}
+
+ * _`definition`_
+ The root definition object.
+ * _`isGroupMode`_ _(optional)_
+ Whether or not the display mode is the 'group' mode.
+ * _`isMergeMode`_
+ Whether or not the display mode is the 'merge' mode.
+
+
+ Example:
+
+ ```handlebars
+ {{~#mergeTags definition group merge}}{{/mergeTags~}}
+ ```
+
+ Output:
+ ```html
+ v5m, vt, JMdict (English)
+ ```
+
+
+
+### `eachUpTo`
+
+Similar to the built-in `each` function, but iterates up to a maximum count.
+If the iterable is falsy or empty, the `else` condition will be used.
+
+
+ Syntax:
+
+ {{#eachUpTo iterable maxCount}}(modification){{else}}(else-modification){{/eachUpTo}}
+
+ * _`iterable`_
+ The object that should be looped over. A JavaScript [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop is used, so the object only needs to be iterable.
+ * _`maxCount`_ _(optional)_
+ The maximum number of entries to loop over.
+ * _`modification`_
+ The template used to modify the value. The context is changed to the current item of iteration.
+ * _`else-modification`_
+ The template used in case the iterable is falsy or empty. The context is unchanged.
+
+
+ Example:
+
+ ```handlebars
+ {{~#eachUpTo someArray 5}}{{{.}}}
{{else}}Empty{{/mergeTags~}}
+ ```
+
+ Output:
+ ```html
+ someArray[0]
someArray[1]
someArray[2]
someArray[3]
someArray[4]
+ ```
+
+ Preview:
+ someArray[0]
someArray[1]
someArray[2]
someArray[3]
someArray[4]
+
+
+
+### `spread`
+
+Uses the JavaScript [spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) operator to convert one or more iterables into a single array.
+This allows it to be used similar to an [`Array.concat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) operation.
+
+
+ Syntax:
+
+ {{#spread iterable1 iterable2 ... iterableN}}{{/spread}}
+
+ * _`iterableN`_
+ A variable amount of iterable objects to combine into a single array.
+
+
+ Example:
+
+ ```handlebars
+ {{#each (spread array1 array2)}}{{{.}}}
{{/each}}
+ ```
+
+ Output:
+ ```html
+ array1[0]
array1[1]
array2[0]
array2[1]
+ ```
+
+ Preview:
+ array1[0]
array1[1]
array2[0]
array2[1]
+
+
+
+### `op`
+
+Performs a simple operation on one, two, or three arguments. The operations available are:
+
+* Unary operators: `+`, `-`, `~`, `!`
+* Binary operators: `+`, `-`, `/`, `*`, `%`, `**`, `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, `>=`, `<<`, `>>`, `>>>`, `&`, `|`, `^`, `&&`, `||`
+* Ternary operators: `?:`
+
+If an unknown operator is specified, the `undefined` value is returned.
+
+
+ Syntax:
+
+ {{#op operator operand1 [operand2] [operand3]}}{{/op}}
+
+ * _`operator`_
+ One of the unary, binary, or ternary operators.
+ * _`operand1`_
+ The first operand of the operation.
+ * _`operand2`_ _(Optional)_
+ The second operand of the operation.
+ * _`operand3`_ _(Optional)_
+ The third operand of the operation.
+
+
+ Example:
+
+ ```handlebars
+ {{#if (op "===" value1 value2)}}Values are equal{{/op~}}
+ {{~#op "-" value1}}{{/op~}}
+ {{~#op "?:" value1 "a" "b"}}{{/op}}
+ ```
+
+ Output:
+ ```html
+ Values are equal
-32
a
+ ```
+
+ Preview:
+ Values are equal
-32
a
+
+
+
+### `get`
+
+Gets a value from the custom state stack.
+
+
+ Syntax:
+
+ {{#get name}}{{/get}}
+
+ * _`name`_
+ The name of the variable to get.
+
+
+ Example:
+
+ ```handlebars
+ {{#get "some-text"}}{{/get}}
+ ```
+
+ Output:
+ ```html
+ This is the value of some-text!
+ ```
+
+
+
+### `set`
+
+Assigns a value to the custom state stack.
+
+
+ Syntax:
+
+ {{#set name}}value{{/get}}
+ {{#set name value}}{{/get}}
+
+ * _`name`_
+ The name of the variable to assign.
+ * _`value`_
+ The value of the variable.
+
+
+ Example:
+
+ ```handlebars
+ {{#set "some-text"}}This is the value of some-text!{{/set~}}
+ {{~#set "some-number" 32}}{{/set}}
+ ```
+
+ Output:
+ ```html
+ ```
+
+
+
+### `scope`
+
+Pushes a new variable scope to the custom state stack.
+Variable assignments are applied to the most recent scope,
+and variable lookups will start from the most recent scope and work backwards until a value is found.
+
+
+ Syntax:
+
+ {{#scope}}content{{/scope}}
+
+ * _`name`_
+ The name of the variable to assign.
+ * _`value`_
+ The value of the variable.
+
+
+ Example:
+
+ ```handlebars
+ {{~#set "key" 32}}{{/set~}}
+ {{~#get "key"}}{{/get~}},
+ {{~#scope~}}
+ {{~#get "key"}}{{/get~}},
+ {{~#set "key" 64}}{{/set~}}
+ {{~#get "key"}}{{/get~}},
+ {{~/scope~}}
+ {{~#get "key"}}{{/get~}}
+ ```
+
+ Output:
+ ```html
+ 32,32,64,32
+ ```
+
+
+
+### `property`
+
+Repeatedly gets a property of an object.
+
+
+ Syntax:
+
+ {{#property object property1 property2 ... propertyN}}{{/property}}
+
+ * _`object`_
+ The initial object to use.
+ * _`propertyN`_
+ A chain of property names to get on the object.
+
+
+ Example:
+
+ ```handlebars
+ {{property someObject "field" 0 "toString"}}
+ ```
+
+ Output:
+ ```html
+ function toString() { [native code] }
+ ```
+
+
+
+### `noop`
+
+No-op. Returns the inner contents of the template.
+
+
+ Syntax:
+
+ {{#noop}}content{{/noop}}
+
+
+ Example:
+
+ ```handlebars
+ {{noop}}Unchanged content{{/noop}}
+ ```
+
+ Output:
+ ```html
+ Unchanged content
+ ```
+
+
+
+### `isMoraPitchHigh`
+
+Returns whether or not a mora will have a high pitch, given the index of the mora and the position of the downstep.
+
+
+ Syntax:
+
+ {{#isMoraPitchHigh index position}}{{/isMoraPitchHigh}}
+
+
+ Example:
+
+ ```handlebars
+ {{#if (isMoraPitchHigh 1 2)}}High pitch{{else}}Low pitch{{/if}}
+ ```
+
+ Output:
+ ```html
+ High pitch
+ ```
+
+
+
+### `getKanaMorae`
+
+Returns an array of the mora for a kana string.
+
+
+ Syntax:
+
+ {{#getKanaMorae kana-string}}{{/getKanaMorae}}
+
+
+ Example:
+
+ ```handlebars
+ {{#each (getKanaMorae "よみちゃん")}}{{{.}}}
{{/each}}
+ ```
+
+ Output:
+ ```html
+ よ
み
ちゃ
ん
+ ```
+
+ Preview:
+ よ
み
ちゃ
ん
+
+
+
+## Legacy Helpers
+
+Yomichan has historically used Handlebars templates to generate the HTML used on the search page and results popup.
+To simplify the and improve Yomichan's capabilities, the HTML elements are now generated directly using a different process.
+
+As such, there are several leftover Handlebars helpers that do not have much utility for Anki templates, but are kept for compatibility purposes.
+
+
+### `kanjiLinks`
+
+Replaces kanji characters in the text with linkified versions.
+
+
+ Syntax:
+
+ {{#kanjiLinks}}text{{/kanjiLinks}}
+
+
+ Example:
+
+ ```handlebars
+ {{#kanjiLinks}}読む{{/kanjiLinks}}
+ ```
+
+ Output:
+ ```html
+ 読む
+ ```
+
+ Preview:
+ 読む
+
+
+
+### `sanitizeCssClass`
+
+Sanitizes text so it can be used as a CSS class name.
+
+
+ Syntax:
+
+ {{#sanitizeCssClass}}text{{/sanitizeCssClass}}
+
+
+ Example:
+
+ ```handlebars
+ {{#sanitizeCssClass}}some text with many types of characters !@#$%^ 読む{{/sanitizeCssClass}}
+ ```
+
+ Output:
+ ```html
+ some_text_with_many_types_of_characters________読む
+ ```
+