diff --git a/docs/Makefile b/docs/Makefile
index 4ef018b..b30b87d 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,4 +1,4 @@
-slugs := index getting-started basic-syntax dynamic-content partials control-structures traits web-frameworks faq
+slugs := index getting-started text-escaping elements-attributes splices-toggles control-structures partials render-trait web-frameworks faq
 
 slug_to_md = content/$(1).md
 slug_to_html = site/$(1).html
diff --git a/docs/content/basic-syntax.md b/docs/content/basic-syntax.md
deleted file mode 100644
index 48b86e5..0000000
--- a/docs/content/basic-syntax.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# Basic syntax
-
-The next few sections will outline the syntax used by Maud templates.
-
-## Literals `""`
-
-Literal strings use the same syntax as Rust. Wrap them in double quotes, and use a backslash for escapes.
-
-```rust
-html! {
-    "Oatmeal, are you crazy?"
-}
-```
-
-### Escaping and `PreEscaped`
-
-By default, HTML special characters are escaped automatically. Wrap the string in `(PreEscaped())` to disable this escaping. (See the section on [dynamic content] to learn more about how this works.)
-
-```rust
-use maud::PreEscaped;
-html! {
-    "<script>alert(\"XSS\")</script>"                // &lt;script&gt;...
-    (PreEscaped("<script>alert(\"XSS\")</script>"))  // <script>...
-}
-```
-
-[dynamic content]: dynamic-content.md
-
-If the string is long, or contains many special characters, then it may be worth using [raw strings] instead:
-
-```rust
-use maud::PreEscaped;
-html! {
-    (PreEscaped(r#"
-        <script>
-            alert("Look ma, no backslashes!");
-        </script>
-    "#))
-}
-```
-
-[raw strings]: https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
-
-If you want to add a `<!DOCTYPE html>` declaration to your page, you may use the `maud::DOCTYPE` constant instead of writing it out by hand:
-
-```rust
-use maud::DOCTYPE;
-html! {
-    (DOCTYPE)  // <!DOCTYPE html>
-}
-```
-
-## Elements `p`
-
-Write an element using curly braces: `p { ... }`.
-
-Terminate a void element using a semicolon: `br;`. Note that the result will be rendered with HTML syntax – `<br>` not `<br />`.
-
-```rust
-html! {
-    h1 { "Poem" }
-    p {
-        "Rock, you are a rock."
-        br;
-        "Gray, you are gray,"
-        br;
-        "Like a rock, which you are."
-        br;
-        "Rock."
-    }
-}
-```
-
-Maud also supports ending a void element with a slash: `br /`. This syntax is [deprecated][#96] and should not be used in new code.
-
-[#96]: https://github.com/lambda-fairy/maud/pull/96
-
-Before version 0.18, Maud allowed the curly braces to be omitted. This syntax was [removed][#137] and now causes an error instead.
-
-[#137]: https://github.com/lambda-fairy/maud/pull/137
-
-## Non-standard HTML ("custom") elements
-
-Maud is not restricted to writing well-known HTML elements like `h1`, `p`, `span`, etc. If you want to use [custom elements][custom-elements], you can.
-
-```rust
-html! {
-    blog-post {
-        title { "My blog" }
-    }
-}
-```
-
-[custom-elements]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
-
-## Non-empty attributes `id="yay"`
-
-```rust
-html! {
-    ul {
-        li {
-            a href="about:blank" { "Apple Bloom" }
-        }
-        li class="lower-middle" {
-            "Sweetie Belle"
-        }
-        li dir="rtl" {
-            "Scootaloo "
-            small { "(also a chicken)" }
-        }
-    }
-}
-```
-
-Add attributes using the syntax: `attr="value"`. You can attach any number of attributes to an element. The values must be quoted: they are parsed as string literals.
-
-## Empty attributes `checked?` `disabled?[foo]`
-
-Declare an empty attribute using a `?` suffix: `checked?`.
-
-```rust
-html! {
-    form {
-        input type="checkbox" name="cupcakes" checked?;
-        " "
-        label for="cupcakes" { "Do you like cupcakes?" }
-    }
-}
-```
-
-To toggle an attribute based on a boolean flag, use a `?[]` suffix instead: `checked?[foo]`. This will check the value of `foo` at runtime, inserting the attribute only if `foo` equals `true`.
-
-```rust
-let allow_editing = true;
-html! {
-    p contenteditable?[allow_editing] {
-        "Edit me, I "
-        em { "dare" }
-        " you."
-    }
-}
-```
-
-## Classes and IDs `.foo` `#bar`
-
-Add classes and IDs to an element using `.foo` and `#bar` syntax. The tag will default to `div` if an element begins with a class or ID. You can chain multiple classes and IDs together, and mix and match them with other attributes:
-
-```rust
-html! {
-    .container#main {
-        input.big.scary.bright-red type="button" value="Launch Party Cannon";
-    }
-}
-```
-
-To toggle a class based on a boolean flag, use a `[]` suffix: `.foo[is_foo]`. This will check the value of `is_foo` at runtime, inserting that class value `foo` in the class attribute only if `is_foo` is `true`.
-
-```rust
-let cuteness = 95;
-html! {
-    p.cute[cuteness > 50] { "Squee!" }
-}
-```
diff --git a/docs/content/control-structures.md b/docs/content/control-structures.md
index b71114d..d66af3a 100644
--- a/docs/content/control-structures.md
+++ b/docs/content/control-structures.md
@@ -28,7 +28,7 @@ html! {
 }
 ```
 
-`@if let` is supported as well. It works as you'd expect:
+`@if let` is supported as well:
 
 ```rust
 let user = Some("Pinkie Pie");
diff --git a/docs/content/elements-attributes.md b/docs/content/elements-attributes.md
new file mode 100644
index 0000000..a5f0f4f
--- /dev/null
+++ b/docs/content/elements-attributes.md
@@ -0,0 +1,101 @@
+# Elements and attributes
+
+## Elements: `p`
+
+Write an element using curly braces: `p { ... }`.
+
+Terminate a void element using a semicolon: `br;`. Note that the result will be rendered with HTML syntax – `<br>` not `<br />`.
+
+```rust
+html! {
+    h1 { "Poem" }
+    p {
+        "Rock, you are a rock."
+        br;
+        "Gray, you are gray,"
+        br;
+        "Like a rock, which you are."
+        br;
+        "Rock."
+    }
+}
+```
+
+Maud also supports ending a void element with a slash: `br /`. This syntax is [deprecated][#96] and should not be used in new code.
+
+[#96]: https://github.com/lambda-fairy/maud/pull/96
+
+Before version 0.18, Maud allowed the curly braces to be omitted. This syntax was [removed][#137] and now causes an error instead.
+
+[#137]: https://github.com/lambda-fairy/maud/pull/137
+
+## Custom elements
+
+Maud also supports [custom elements].
+
+```rust
+html! {
+    blog-post {
+        title { "My blog" }
+    }
+}
+```
+
+[custom elements]: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
+
+## Non-empty attributes: `title="yay"`
+
+Add attributes using the syntax: `attr="value"`. You can attach any number of attributes to an element. The values must be quoted: they are parsed as string literals.
+
+```rust
+html! {
+    ul {
+        li {
+            a href="about:blank" { "Apple Bloom" }
+        }
+        li class="lower-middle" {
+            "Sweetie Belle"
+        }
+        li dir="rtl" {
+            "Scootaloo "
+            small { "(also a chicken)" }
+        }
+    }
+}
+```
+
+## Empty attributes: `checked?`
+
+Declare an empty attribute using a `?` suffix: `checked?`.
+
+```rust
+html! {
+    form {
+        input type="checkbox" name="cupcakes" checked?;
+        " "
+        label for="cupcakes" { "Do you like cupcakes?" }
+    }
+}
+```
+
+## Classes and IDs: `.foo` `#bar`
+
+Add classes and IDs to an element using `.foo` and `#bar` syntax. You can chain multiple classes and IDs together, and mix and match them with other attributes:
+
+```rust
+html! {
+    input#cannon.big.scary.bright-red type="button" value="Launch Party Cannon";
+}
+```
+
+## Implicit `div` elements
+
+If the element name is omitted, but there is a class or ID, then it is assumed to be a `div`.
+
+```rust
+html! {
+    #main {
+        "Main content!"
+    }
+}
+```
diff --git a/docs/content/faq.md b/docs/content/faq.md
index 6f610c0..931f606 100644
--- a/docs/content/faq.md
+++ b/docs/content/faq.md
@@ -2,7 +2,9 @@
 
 ## What is the origin of the name "Maud"?
 
-Maud is named after a [character](http://mlp.wikia.com/wiki/Maud_Pie) from *My Little Pony: Friendship is Magic*. It does not refer to the [poem](https://en.wikipedia.org/wiki/Maud_and_other_poems) by Alfred Tennyson, though other people have brought that up in the past.
+Maud is named after a [character] from *My Little Pony: Friendship is Magic*.
+It does not refer to the [poem] by Alfred Tennyson,
+though other people have brought that up in the past.
 
 Here are some reasons why I chose this name:
 
@@ -10,28 +12,63 @@ Here are some reasons why I chose this name:
 
 * The library is efficient and austere, like the character;
 
-* Google used to maintain a site called ["HTML5 Rocks"](https://techcrunch.com/2010/06/22/html5rocks-google/), and Maud (the character) is a geologist.
+* Google used to maintain a site called ["HTML5 Rocks"],
+  and Maud (the character) is a geologist.
+
+[character]: http://mlp.wikia.com/wiki/Maud_Pie
+[poem]: https://en.wikipedia.org/wiki/Maud_and_other_poems
+["HTML5 Rocks"]: https://techcrunch.com/2010/06/22/html5rocks-google/
 
 ## Why does `html!` always allocate a `String`? Wouldn't it be more efficient if it wrote to a handle directly?
 
 Good question! In fact, Maud did work this way in the past.
 
-Sadly, that kind of thing didn't work out that well in practice. Having to pass the handle around made templates hard to compose, which is important in any non-trivial project. Furthermore, Iron (and other middleware frameworks) likes to take ownership of the response body, so we'd need to do some closure gymnastics to get everything to work together. To put the nail in the coffin, benchmarks showed that a `String` based solution is actually faster than one which avoids allocations.
+But it's hard to support buffer reuse in an ergonomic way.
+The approaches I tried
+either involved too much boilerplate,
+or caused mysterious lifetime issues,
+or both.
+Moreover, Maud's allocation pattern—with small, short-lived buffers—follow the fast path in modern allocators.
+These reasons are why I changed `html!` to return a `String` in version 0.11.
 
-For these reasons, I changed `html!` to return a `String` in version 0.11.
+That said,
+Rust has changed a lot since then,
+and some of those old assumptions
+might no longer hold today.
+So this decision could be revisited
+prior to the 1.0 release.
 
 ## Why is Maud written as a procedural macro? Can't it use `macro_rules!` instead?
 
-This is certainly possible, and in fact the [Horrorshow](https://github.com/Stebalien/horrorshow-rs) library works this way.
+This is certainly possible, and indeed the [Horrorshow] library works this way.
 
-I use procedural macros because they are more flexible. There are some syntax constructs in Maud that cannot be parsed with `macro_rules!`; better diagnostics are a bonus as well.
+I use procedural macros because they are more flexible.
+There are some syntax constructs in Maud that are hard to parse with `macro_rules!`;
+better diagnostics are a bonus as well.
+
+[Horrorshow]: https://github.com/Stebalien/horrorshow-rs
 
 ## Maud has had a lot of releases so far. When will it reach 1.0?
 
-I plan to make a 1.0 release when the library can be used on stable Rust.
+I originally planned to cut a 1.0
+after implementing stable support.
+But now that's happened,
+I've realized that there are a couple design questions
+that I'd like to resolve
+before marking that milestone.
+Expect a blog post on this topic Very Soon®.
 
-## Why doesn't Maud implement [context-aware escaping](https://security.googleblog.com/2009/03/reducing-xss-by-way-of-automatic.html)?
+## Why doesn't Maud implement [context-aware escaping]?
 
-If a project follows best practices in separating HTML and CSS/JavaScript, then context-aware escaping is unnecessary.
+I agree that context-aware escaping is very important,
+especially for the kind of small-scale development
+that Maud is used for.
+But it's a complex feature,
+with security implications,
+so I want to take the time
+to get it right.
 
-Google uses context-aware escaping because it has a large, decades-old code base, much of it written before these best practices were well known. Any project that uses Maud is neither large nor decades-old, and so should not have the need for this feature.
+Please follow [#181] for the latest developments!
+
+[context-aware escaping]: https://security.googleblog.com/2009/03/reducing-xss-by-way-of-automatic.html
+[#181]: https://github.com/lambda-fairy/maud/issues/181
diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md
index fb38a96..b24fdd3 100644
--- a/docs/content/getting-started.md
+++ b/docs/content/getting-started.md
@@ -1,18 +1,5 @@
 # Getting started
 
-## Which version of Rust?
-
-While Maud works well
-on both stable and [nightly] versions
-of Rust,
-the error messages are slightly better
-on nightly.
-For this reason,
-it is recommended to develop using nightly Rust
-but test and deploy using stable.
-
-[nightly]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
-
 ## Add Maud to your project
 
 Once Rust is set up,
@@ -33,7 +20,6 @@ maud = "*"
 Then save the following to `src/main.rs`:
 
 ```rust
-extern crate maud;
 use maud::html;
 
 fn main() {
@@ -56,3 +42,16 @@ Run this program with `cargo run`, and you should get the following:
 ```
 
 Congrats – you've written your first Maud program!
+
+## Which version of Rust?
+
+While Maud works well
+on both stable and [nightly] versions
+of Rust,
+the error messages are slightly better
+on nightly.
+For this reason,
+it is recommended to develop using nightly Rust,
+but test and deploy using stable.
+
+[nightly]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html
diff --git a/docs/content/partials.md b/docs/content/partials.md
index d3d9e21..520e13c 100644
--- a/docs/content/partials.md
+++ b/docs/content/partials.md
@@ -33,11 +33,8 @@ pub fn page(title: &str, greeting_box: Markup) -> Markup {
     html! {
         // Add the header markup to the page
         (header(title))
-        body {
-            h1 { "Hello World" }
-            (greeting_box)
-        }
-        // Add the footer markup to the page
+        h1 { (title) }
+        (greeting_box)
         (footer())
     }
 }
diff --git a/docs/content/traits.md b/docs/content/render-trait.md
similarity index 94%
rename from docs/content/traits.md
rename to docs/content/render-trait.md
index 344eafb..ed41680 100644
--- a/docs/content/traits.md
+++ b/docs/content/render-trait.md
@@ -1,6 +1,6 @@
 # The `Render` trait
 
-By default, a `(splice)` is rendered using the [`std::fmt::Display`][Display] trait, with any HTML special characters escaped automatically.
+By default, a [`(splice)`](splices-toggles.md) is rendered using the [`std::fmt::Display`][Display] trait, with any HTML special characters escaped automatically.
 
 To change this behavior, implement the [`Render`][Render] trait for your type. Then, when a value of this type is used in a template, Maud will call your custom code instead.
 
diff --git a/docs/content/dynamic-content.md b/docs/content/splices-toggles.md
similarity index 72%
rename from docs/content/dynamic-content.md
rename to docs/content/splices-toggles.md
index f51fba3..dc40891 100644
--- a/docs/content/dynamic-content.md
+++ b/docs/content/splices-toggles.md
@@ -1,6 +1,8 @@
-# Dynamic content
+# Splices and toggles
 
-Use `(foo)` syntax to splice in the value of `foo` at runtime. Any HTML special characters are escaped by default.
+## Splices: `(foo)`
+
+Use `(foo)` syntax to insert the value of `foo` at runtime. Any HTML special characters are escaped by default.
 
 ```rust
 let best_pony = "Pinkie Pie";
@@ -27,7 +29,7 @@ html! {
 }
 ```
 
-## Splices in attributes
+### Splices in attributes
 
 Splices work in attributes as well.
 
@@ -51,11 +53,11 @@ html! {
 }
 ```
 
-## What can be spliced?
+### What can be spliced?
 
 You can splice any value that implements [`std::fmt::Display`][Display]. Most primitive types (such as `str` and `i32`) implement this trait, so they should work out of the box.
 
-To change this behavior for some type, you can implement the [`Render`][Render] trait by hand. The [`PreEscaped`][PreEscaped] wrapper type, which outputs its argument without escaping, works this way. See the [traits](./traits.md) section for details.
+To change this behavior for some type, you can implement the [`Render`][Render] trait by hand. The [`PreEscaped`][PreEscaped] wrapper type, which outputs its argument without escaping, works this way. See the [traits](render-trait.md) section for details.
 
 ```rust
 use maud::PreEscaped;
@@ -69,3 +71,29 @@ html! {
 [Display]: http://doc.rust-lang.org/std/fmt/trait.Display.html
 [Render]: https://docs.rs/maud/*/maud/trait.Render.html
 [PreEscaped]: https://docs.rs/maud/*/maud/struct.PreEscaped.html
+
+## Toggles: `[foo]`
+
+Use `[foo]` syntax to show or hide something based on a boolean expression `foo`.
+
+This works on empty attributes:
+
+```rust
+let allow_editing = true;
+html! {
+    p contenteditable?[allow_editing] {
+        "Edit me, I "
+        em { "dare" }
+        " you."
+    }
+}
+```
+
+And classes:
+
+```rust
+let cuteness = 95;
+html! {
+    p.cute[cuteness > 50] { "Squee!" }
+}
+```
diff --git a/docs/content/text-escaping.md b/docs/content/text-escaping.md
new file mode 100644
index 0000000..600f962
--- /dev/null
+++ b/docs/content/text-escaping.md
@@ -0,0 +1,55 @@
+# Text and escaping
+
+## Text
+
+Literal strings use the same syntax as Rust. Wrap them in double quotes, and use a backslash for escapes.
+
+```rust
+html! {
+    "Oatmeal, are you crazy?"
+}
+```
+
+## Raw strings
+
+If the string is long, or contains many special characters, then it may be worth using [raw strings] instead:
+
+```rust
+html! {
+    pre {
+        r#"
+            Rocks, these are my rocks.
+            Sediments make me sedimental.
+            Smooth and round,
+            Asleep in the ground.
+            Shades of brown
+            And gray.
+        "#
+    }
+}
+```
+
+[raw strings]: https://doc.rust-lang.org/reference/tokens.html#raw-string-literals
+
+## Escaping and `PreEscaped`
+
+By default, HTML special characters are escaped automatically. Wrap the string in `(PreEscaped())` to disable this escaping. (See the section on [splices](splices-toggles.md) to learn more about how this works.)
+
+```rust
+use maud::PreEscaped;
+html! {
+    "<script>alert(\"XSS\")</script>"                // &lt;script&gt;...
+    (PreEscaped("<script>alert(\"XSS\")</script>"))  // <script>...
+}
+```
+
+## The `DOCTYPE` constant
+
+If you want to add a `<!DOCTYPE html>` declaration to your page, you may use the `maud::DOCTYPE` constant instead of writing it out by hand:
+
+```rust
+use maud::DOCTYPE;
+html! {
+    (DOCTYPE)  // <!DOCTYPE html>
+}
+```