diff --git a/benches/bench.typ b/benches/bench.typ
index 75c97d025..4925db4d8 100644
--- a/benches/bench.typ
+++ b/benches/bench.typ
@@ -4,15 +4,15 @@
 // There are variables and they can take normal values like strings, ...
 #let city = "Berlin"
 
-// ... but also "template" values. While these contain markup,
+// ... but also "content" values. While these contain markup,
 // they are also values and can be summed, stored in arrays etc.
 // There are also more standard control flow structures, like #if and #for.
 #let university = [*Technische Universität {city}*]
 #let faculty = [*Fakultät II, Institut for Mathematik*]
 
 // The `box` function just places content into a rectangular container. When
-// the only argument to a function is a template, the parentheses can be omitted
-// (i.e. `f[a]` is the same as `f([a])`).
+// the only argument to a function is a content block, the parentheses can be
+// omitted (i.e. `f[a]` is the same as `f([a])`).
 #box[
   // Backslash adds a forced line break.
   #university \
@@ -26,8 +26,8 @@
 // Adds vertical spacing.
 #v(6mm)
 
-// If the last argument to a function is a template, we can also place it behind
-// the parentheses.
+// If the last argument to a function is a content block, we can also place it
+// behind the parentheses.
 #align(center)[
   // Markdown-like syntax for headings.
   ==== 3. Übungsblatt Computerorientierte Mathematik II #v(4mm)
diff --git a/benches/oneshot.rs b/benches/oneshot.rs
index 9bce185c0..ddd689703 100644
--- a/benches/oneshot.rs
+++ b/benches/oneshot.rs
@@ -80,7 +80,7 @@ fn bench_eval(iai: &mut Iai) {
 fn bench_layout(iai: &mut Iai) {
     let (mut ctx, id) = context();
     let module = ctx.evaluate(id).unwrap();
-    iai.run(|| module.template.layout(&mut ctx));
+    iai.run(|| module.content.layout(&mut ctx));
 }
 
 fn bench_render(iai: &mut Iai) {
diff --git a/src/eval/array.rs b/src/eval/array.rs
index 7ff5fb74c..294d28711 100644
--- a/src/eval/array.rs
+++ b/src/eval/array.rs
@@ -89,7 +89,7 @@ impl Array {
             a.partial_cmp(b).unwrap_or_else(|| {
                 if result.is_ok() {
                     result = Err(format!(
-                        "cannot compare {} with {}",
+                        "cannot order {} and {}",
                         a.type_name(),
                         b.type_name(),
                     ));
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
index b27aceb72..4e8d76046 100644
--- a/src/eval/capture.rs
+++ b/src/eval/capture.rs
@@ -49,8 +49,8 @@ impl<'a> CapturesVisitor<'a> {
             // through the expressions that contain them).
             Some(Expr::Ident(ident)) => self.capture(ident),
 
-            // Blocks and templates create a scope.
-            Some(Expr::Code(_) | Expr::Template(_)) => {
+            // Code and content blocks create a scope.
+            Some(Expr::Code(_) | Expr::Content(_)) => {
                 self.internal.enter();
                 for child in node.children() {
                     self.visit(child);
diff --git a/src/eval/class.rs b/src/eval/class.rs
index 2cced74d5..051916673 100644
--- a/src/eval/class.rs
+++ b/src/eval/class.rs
@@ -2,24 +2,25 @@ use std::any::TypeId;
 use std::fmt::{self, Debug, Formatter, Write};
 use std::hash::{Hash, Hasher};
 
-use super::{Args, Func, StyleMap, Template, Value};
+use super::{Args, Content, Func, StyleMap, Value};
 use crate::diag::TypResult;
 use crate::Context;
 
 /// A class of nodes.
 ///
 /// You can [construct] an instance of a class in Typst code by invoking the
-/// class as a callable. This always produces a template value, but not
+/// class as a callable. This always produces a content value, but not
 /// necessarily a simple inline or block node. For example, the `text`
 /// constructor does not actually create a [`TextNode`]. Instead it applies
-/// styling to whatever node you pass in and returns it structurally unchanged.
+/// styling to whatever content you pass in and returns it structurally
+/// unchanged.
 ///
 /// The arguments you can pass to a class constructor fall into two categories:
 /// Data that is inherent to the instance (e.g. the text/content of a heading)
 /// and style properties (e.g. the fill color of a heading). As the latter are
 /// often shared by many instances throughout a document, they can also be
 /// conveniently configured through class's [`set`] rule. Then, they apply to
-/// all nodes that are instantiated into the template where the `set` was
+/// all nodes that are instantiated into the content block where the `set` was
 /// executed.
 ///
 /// ```typst
@@ -55,8 +56,8 @@ impl Class {
             construct: |ctx, args| {
                 let mut styles = StyleMap::new();
                 T::set(args, &mut styles)?;
-                let template = T::construct(ctx, args)?;
-                Ok(Value::Template(template.styled_with_map(styles.scoped())))
+                let content = T::construct(ctx, args)?;
+                Ok(Value::Content(content.styled_with_map(styles.scoped())))
             },
             set: T::set,
         }
@@ -80,8 +81,8 @@ impl Class {
     /// Construct an instance of the class.
     ///
     /// This parses both property and data arguments (in this order), styles the
-    /// template constructed from the data with the style properties and wraps
-    /// it in a value.
+    /// content constructed from the data with the style properties and wraps it
+    /// in a value.
     pub fn construct(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> {
         let value = (self.construct)(ctx, &mut args)?;
         args.finish()?;
@@ -126,7 +127,7 @@ pub trait Construct {
     ///
     /// This is passed only the arguments that remain after execution of the
     /// class's set rule.
-    fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Template>;
+    fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
 }
 
 /// Set style properties of a class.
diff --git a/src/eval/template.rs b/src/eval/content.rs
similarity index 80%
rename from src/eval/template.rs
rename to src/eval/content.rs
index 3e88b6d7c..b6d60957f 100644
--- a/src/eval/template.rs
+++ b/src/eval/content.rs
@@ -20,29 +20,23 @@ use crate::util::EcoString;
 ///
 /// This results from:
 /// - anything written between square brackets in Typst
-/// - any class constructor
+/// - any node constructor
 ///
-/// This enum has two notable variants:
+/// Content is represented as a tree of nodes. There are two nodes of special
+/// interest:
 ///
-/// 1. A `Styled` template attaches a style map to a template. This map affects
-///    the whole subtemplate. For example, a single bold word could be
-///    represented as a `Styled(Text("Hello"), [TextNode::STRONG: true])`
-///    template.
-///
-/// 2. A `Sequence` template combines multiple arbitrary templates and is the
-///    representation of a "flow" of content. So, when you write `[Hi] + [you]`
-///    in Typst, this type's [`Add`] implementation is invoked and the two
-///    [`Text`](Self::Text) templates are combined into a single
-///    [`Sequence`](Self::Sequence) template.
-///
-///    A sequence may contain nested sequences (meaning this variant effectively
-///    allows templates to form trees). All nested sequences can equivalently be
-///    represented as a single flat sequence, but allowing nesting doesn't hurt
-///    since we can just recurse into the nested sequences. Also, in theory,
-///    this allows better complexity when adding large sequence nodes just like
-///    for something like a text rope.
+/// 1. A `Styled` node attaches a style map to other content. For example, a
+///    single bold word could be represented as a `Styled(Text("Hello"),
+///    [TextNode::STRONG: true])` node.
+
+/// 2. A `Sequence` node content combines other arbitrary content and is the
+///    representation of a "flow" of other nodes. So, when you write `[Hi] +
+///    [you]` in Typst, this type's [`Add`] implementation is invoked and the
+///    two [`Text`](Self::Text) nodes are combined into a single
+///    [`Sequence`](Self::Sequence) node. A sequence may contain nested
+///    sequences.
 #[derive(PartialEq, Clone, Hash)]
-pub enum Template {
+pub enum Content {
     /// A word space.
     Space,
     /// A line break.
@@ -71,19 +65,19 @@ pub enum Template {
     Page(PageNode),
     /// A node that can be realized with styles.
     Show(ShowNode),
-    /// A template with attached styles.
+    /// Content with attached styles.
     Styled(Arc<(Self, StyleMap)>),
-    /// A sequence of multiple subtemplates.
+    /// A sequence of multiple nodes.
     Sequence(Arc<Vec<Self>>),
 }
 
-impl Template {
-    /// Create an empty template.
+impl Content {
+    /// Create empty content.
     pub fn new() -> Self {
         Self::sequence(vec![])
     }
 
-    /// Create a template from an inline-level node.
+    /// Create content from an inline-level node.
     pub fn inline<T>(node: T) -> Self
     where
         T: Layout + Debug + Hash + Sync + Send + 'static,
@@ -91,7 +85,7 @@ impl Template {
         Self::Inline(node.pack())
     }
 
-    /// Create a template from a block-level node.
+    /// Create content from a block-level node.
     pub fn block<T>(node: T) -> Self
     where
         T: Layout + Debug + Hash + Sync + Send + 'static,
@@ -99,7 +93,7 @@ impl Template {
         Self::Block(node.pack())
     }
 
-    /// Create a template from a showable node.
+    /// Create content from a showable node.
     pub fn show<T>(node: T) -> Self
     where
         T: Show + Debug + Hash + Sync + Send + 'static,
@@ -107,7 +101,7 @@ impl Template {
         Self::Show(node.pack())
     }
 
-    /// Style this template with a single property.
+    /// Style this content with a single style property.
     pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
         if let Self::Styled(styled) = &mut self {
             if let Some((_, map)) = Arc::get_mut(styled) {
@@ -121,7 +115,7 @@ impl Template {
         self.styled_with_map(StyleMap::with(key, value))
     }
 
-    /// Style this template with a full style map.
+    /// Style this content with a full style map.
     pub fn styled_with_map(mut self, styles: StyleMap) -> Self {
         if styles.is_empty() {
             return self;
@@ -139,17 +133,17 @@ impl Template {
         Self::Styled(Arc::new((self, styles)))
     }
 
-    /// Style this template in monospace.
+    /// Style this content in monospace.
     pub fn monospaced(self) -> Self {
         self.styled(TextNode::MONOSPACED, true)
     }
 
-    /// Underline this template.
+    /// Underline this content.
     pub fn underlined(self) -> Self {
         Self::show(DecoNode::<UNDERLINE>(self))
     }
 
-    /// Create a new sequence template.
+    /// Create a new sequence nodes from multiples nodes.
     pub fn sequence(seq: Vec<Self>) -> Self {
         if seq.len() == 1 {
             seq.into_iter().next().unwrap()
@@ -158,15 +152,15 @@ impl Template {
         }
     }
 
-    /// Repeat this template `n` times.
+    /// Repeat this content `n` times.
     pub fn repeat(&self, n: i64) -> StrResult<Self> {
         let count = usize::try_from(n)
-            .map_err(|_| format!("cannot repeat this template {} times", n))?;
+            .map_err(|_| format!("cannot repeat this content {} times", n))?;
 
         Ok(Self::sequence(vec![self.clone(); count]))
     }
 
-    /// Layout this template into a collection of pages.
+    /// Layout this content into a collection of pages.
     pub fn layout(&self, ctx: &mut Context) -> TypResult<Vec<Arc<Frame>>> {
         let sya = Arena::new();
         let tpa = Arena::new();
@@ -190,13 +184,13 @@ impl Template {
     }
 }
 
-impl Default for Template {
+impl Default for Content {
     fn default() -> Self {
         Self::new()
     }
 }
 
-impl Add for Template {
+impl Add for Content {
     type Output = Self;
 
     fn add(self, rhs: Self) -> Self::Output {
@@ -222,19 +216,19 @@ impl Add for Template {
     }
 }
 
-impl AddAssign for Template {
+impl AddAssign for Content {
     fn add_assign(&mut self, rhs: Self) {
         *self = std::mem::take(self) + rhs;
     }
 }
 
-impl Sum for Template {
+impl Sum for Content {
     fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
         Self::sequence(iter.collect())
     }
 }
 
-impl Layout for Template {
+impl Layout for Content {
     fn layout(
         &self,
         ctx: &mut Context,
@@ -254,18 +248,18 @@ impl Layout for Template {
 
     fn pack(self) -> LayoutNode {
         match self {
-            Template::Block(node) => node,
+            Content::Block(node) => node,
             other => LayoutNode::new(other),
         }
     }
 }
 
-/// Builds a flow or page nodes from a template.
+/// Builds a flow or page nodes from content.
 struct Builder<'a> {
     /// An arena where intermediate style chains are stored.
     sya: &'a Arena<StyleChain<'a>>,
-    /// An arena where intermediate templates are stored.
-    tpa: &'a Arena<Template>,
+    /// An arena where intermediate content resulting from show rules is stored.
+    tpa: &'a Arena<Content>,
     /// The already built page runs.
     pages: Option<StyleVecBuilder<'a, PageNode>>,
     /// The currently built list.
@@ -280,7 +274,7 @@ struct Builder<'a> {
 
 impl<'a> Builder<'a> {
     /// Prepare the builder.
-    fn new(sya: &'a Arena<StyleChain<'a>>, tpa: &'a Arena<Template>, top: bool) -> Self {
+    fn new(sya: &'a Arena<StyleChain<'a>>, tpa: &'a Arena<Content>, top: bool) -> Self {
         Self {
             sya,
             tpa,
@@ -292,33 +286,33 @@ impl<'a> Builder<'a> {
         }
     }
 
-    /// Process a template.
+    /// Process content.
     fn process(
         &mut self,
         ctx: &mut Context,
-        template: &'a Template,
+        content: &'a Content,
         styles: StyleChain<'a>,
     ) -> TypResult<()> {
         if let Some(builder) = &mut self.list {
-            match template {
-                Template::Space => {
-                    builder.staged.push((template, styles));
+            match content {
+                Content::Space => {
+                    builder.staged.push((content, styles));
                     return Ok(());
                 }
-                Template::Parbreak => {
-                    builder.staged.push((template, styles));
+                Content::Parbreak => {
+                    builder.staged.push((content, styles));
                     return Ok(());
                 }
-                Template::List(item) if builder.kind == UNORDERED => {
+                Content::List(item) if builder.kind == UNORDERED => {
                     builder.wide |=
-                        builder.staged.iter().any(|&(t, _)| *t == Template::Parbreak);
+                        builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak);
                     builder.staged.clear();
                     builder.items.push(item.clone());
                     return Ok(());
                 }
-                Template::Enum(item) if builder.kind == ORDERED => {
+                Content::Enum(item) if builder.kind == ORDERED => {
                     builder.wide |=
-                        builder.staged.iter().any(|&(t, _)| *t == Template::Parbreak);
+                        builder.staged.iter().any(|&(t, _)| *t == Content::Parbreak);
                     builder.staged.clear();
                     builder.items.push(item.clone());
                     return Ok(());
@@ -327,14 +321,14 @@ impl<'a> Builder<'a> {
             }
         }
 
-        match template {
-            Template::Space => {
+        match content {
+            Content::Space => {
                 self.par.weak(ParChild::Text(' '.into()), 0, styles);
             }
-            Template::Linebreak => {
+            Content::Linebreak => {
                 self.par.destructive(ParChild::Text('\n'.into()), styles);
             }
-            Template::Horizontal(kind) => {
+            Content::Horizontal(kind) => {
                 let child = ParChild::Spacing(*kind);
                 if kind.is_fractional() {
                     self.par.destructive(child, styles);
@@ -342,21 +336,21 @@ impl<'a> Builder<'a> {
                     self.par.ignorant(child, styles);
                 }
             }
-            Template::Text(text) => {
+            Content::Text(text) => {
                 self.par.supportive(ParChild::Text(text.clone()), styles);
             }
-            Template::Inline(node) => {
+            Content::Inline(node) => {
                 self.par.supportive(ParChild::Node(node.clone()), styles);
             }
-            Template::Parbreak => {
+            Content::Parbreak => {
                 self.finish_par(styles);
                 self.flow.weak(FlowChild::Parbreak, 1, styles);
             }
-            Template::Colbreak => {
+            Content::Colbreak => {
                 self.finish_par(styles);
                 self.flow.destructive(FlowChild::Colbreak, styles);
             }
-            Template::Vertical(kind) => {
+            Content::Vertical(kind) => {
                 self.finish_par(styles);
                 let child = FlowChild::Spacing(*kind);
                 if kind.is_fractional() {
@@ -365,7 +359,7 @@ impl<'a> Builder<'a> {
                     self.flow.ignorant(child, styles);
                 }
             }
-            Template::Block(node) => {
+            Content::Block(node) => {
                 self.finish_par(styles);
                 let child = FlowChild::Node(node.clone());
                 if node.is::<PlaceNode>() {
@@ -375,7 +369,7 @@ impl<'a> Builder<'a> {
                 }
                 self.finish_par(styles);
             }
-            Template::List(item) => {
+            Content::List(item) => {
                 self.list = Some(ListBuilder {
                     styles,
                     kind: UNORDERED,
@@ -384,7 +378,7 @@ impl<'a> Builder<'a> {
                     staged: vec![],
                 });
             }
-            Template::Enum(item) => {
+            Content::Enum(item) => {
                 self.list = Some(ListBuilder {
                     styles,
                     kind: ORDERED,
@@ -393,22 +387,22 @@ impl<'a> Builder<'a> {
                     staged: vec![],
                 });
             }
-            Template::Pagebreak => {
+            Content::Pagebreak => {
                 self.finish_page(ctx, true, true, styles)?;
             }
-            Template::Page(page) => {
+            Content::Page(page) => {
                 self.finish_page(ctx, false, false, styles)?;
                 if let Some(pages) = &mut self.pages {
                     pages.push(page.clone(), styles);
                 }
             }
-            Template::Show(node) => {
+            Content::Show(node) => {
                 let id = node.id();
-                let template = node.show(ctx, styles)?;
-                let stored = self.tpa.alloc(template);
+                let content = node.show(ctx, styles)?;
+                let stored = self.tpa.alloc(content);
                 self.process(ctx, stored, styles.unscoped(id))?;
             }
-            Template::Styled(styled) => {
+            Content::Styled(styled) => {
                 let (sub, map) = styled.as_ref();
                 let stored = self.sya.alloc(styles);
                 let styles = map.chain(stored);
@@ -432,7 +426,7 @@ impl<'a> Builder<'a> {
                     None => {}
                 }
             }
-            Template::Sequence(seq) => {
+            Content::Sequence(seq) => {
                 for sub in seq.iter() {
                     self.process(ctx, sub, styles)?;
                 }
@@ -487,15 +481,15 @@ impl<'a> Builder<'a> {
             None => return Ok(()),
         };
 
-        let template = match kind {
-            UNORDERED => Template::show(ListNode::<UNORDERED> { start: 1, wide, items }),
-            ORDERED | _ => Template::show(ListNode::<ORDERED> { start: 1, wide, items }),
+        let content = match kind {
+            UNORDERED => Content::show(ListNode::<UNORDERED> { start: 1, wide, items }),
+            ORDERED | _ => Content::show(ListNode::<ORDERED> { start: 1, wide, items }),
         };
 
-        let stored = self.tpa.alloc(template);
+        let stored = self.tpa.alloc(content);
         self.process(ctx, stored, styles)?;
-        for (template, styles) in staged {
-            self.process(ctx, template, styles)?;
+        for (content, styles) in staged {
+            self.process(ctx, content, styles)?;
         }
 
         Ok(())
@@ -535,10 +529,10 @@ struct ListBuilder<'a> {
     kind: ListKind,
     items: Vec<ListItem>,
     wide: bool,
-    staged: Vec<(&'a Template, StyleChain<'a>)>,
+    staged: Vec<(&'a Content, StyleChain<'a>)>,
 }
 
-impl Debug for Template {
+impl Debug for Content {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         match self {
             Self::Space => f.pad("Space"),
diff --git a/src/eval/layout.rs b/src/eval/layout.rs
index 135e5e048..94375c61e 100644
--- a/src/eval/layout.rs
+++ b/src/eval/layout.rs
@@ -19,7 +19,7 @@ use crate::Context;
 /// Layout return one frame per used region alongside constraints that define
 /// whether the result is reusable in other regions.
 pub trait Layout {
-    /// Layout the node into the given regions, producing constrained frames.
+    /// Layout this node into the given regions, producing constrained frames.
     fn layout(
         &self,
         ctx: &mut Context,
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 6a918dbde..79c0ad83f 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -11,6 +11,7 @@ mod styles;
 mod capture;
 mod class;
 mod collapse;
+mod content;
 mod control;
 mod func;
 mod layout;
@@ -18,12 +19,12 @@ mod module;
 mod ops;
 mod scope;
 mod show;
-mod template;
 
 pub use array::*;
 pub use capture::*;
 pub use class::*;
 pub use collapse::*;
+pub use content::*;
 pub use control::*;
 pub use dict::*;
 pub use func::*;
@@ -32,7 +33,6 @@ pub use module::*;
 pub use scope::*;
 pub use show::*;
 pub use styles::*;
-pub use template::*;
 pub use value::*;
 
 use unicode_segmentation::UnicodeSegmentation;
@@ -58,7 +58,7 @@ pub trait Eval {
 pub type EvalResult<T> = Result<T, Control>;
 
 impl Eval for Markup {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
         eval_markup(ctx, scp, &mut self.nodes())
@@ -70,7 +70,7 @@ fn eval_markup(
     ctx: &mut Context,
     scp: &mut Scopes,
     nodes: &mut impl Iterator<Item = MarkupNode>,
-) -> EvalResult<Template> {
+) -> EvalResult<Content> {
     let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
 
     while let Some(node) = nodes.next() {
@@ -92,18 +92,18 @@ fn eval_markup(
         });
     }
 
-    Ok(Template::sequence(seq))
+    Ok(Content::sequence(seq))
 }
 
 impl Eval for MarkupNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
         Ok(match self {
-            Self::Space => Template::Space,
-            Self::Linebreak => Template::Linebreak,
-            Self::Parbreak => Template::Parbreak,
-            Self::Text(text) => Template::Text(text.clone()),
+            Self::Space => Content::Space,
+            Self::Linebreak => Content::Linebreak,
+            Self::Parbreak => Content::Parbreak,
+            Self::Text(text) => Content::Text(text.clone()),
             Self::Strong(strong) => strong.eval(ctx, scp)?,
             Self::Emph(emph) => emph.eval(ctx, scp)?,
             Self::Raw(raw) => raw.eval(ctx, scp)?,
@@ -117,45 +117,45 @@ impl Eval for MarkupNode {
 }
 
 impl Eval for StrongNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::show(library::text::StrongNode(
+        Ok(Content::show(library::text::StrongNode(
             self.body().eval(ctx, scp)?,
         )))
     }
 }
 
 impl Eval for EmphNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::show(library::text::EmphNode(
+        Ok(Content::show(library::text::EmphNode(
             self.body().eval(ctx, scp)?,
         )))
     }
 }
 
 impl Eval for RawNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> {
-        let template = Template::show(library::text::RawNode {
+        let content = Content::show(library::text::RawNode {
             text: self.text.clone(),
             block: self.block,
         });
         Ok(match self.lang {
-            Some(_) => template.styled(library::text::RawNode::LANG, self.lang.clone()),
-            None => template,
+            Some(_) => content.styled(library::text::RawNode::LANG, self.lang.clone()),
+            None => content,
         })
     }
 }
 
 impl Eval for MathNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::show(library::math::MathNode {
+        Ok(Content::show(library::math::MathNode {
             formula: self.formula.clone(),
             display: self.display,
         }))
@@ -163,10 +163,10 @@ impl Eval for MathNode {
 }
 
 impl Eval for HeadingNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::show(library::structure::HeadingNode {
+        Ok(Content::show(library::structure::HeadingNode {
             body: self.body().eval(ctx, scp)?,
             level: self.level(),
         }))
@@ -174,10 +174,10 @@ impl Eval for HeadingNode {
 }
 
 impl Eval for ListNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::List(library::structure::ListItem {
+        Ok(Content::List(library::structure::ListItem {
             number: None,
             body: Box::new(self.body().eval(ctx, scp)?),
         }))
@@ -185,10 +185,10 @@ impl Eval for ListNode {
 }
 
 impl Eval for EnumNode {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
-        Ok(Template::Enum(library::structure::ListItem {
+        Ok(Content::Enum(library::structure::ListItem {
             number: self.number(),
             body: Box::new(self.body().eval(ctx, scp)?),
         }))
@@ -203,7 +203,7 @@ impl Eval for Expr {
             Self::Lit(v) => v.eval(ctx, scp),
             Self::Ident(v) => v.eval(ctx, scp),
             Self::Code(v) => v.eval(ctx, scp),
-            Self::Template(v) => v.eval(ctx, scp).map(Value::Template),
+            Self::Content(v) => v.eval(ctx, scp).map(Value::Content),
             Self::Array(v) => v.eval(ctx, scp).map(Value::Array),
             Self::Dict(v) => v.eval(ctx, scp).map(Value::Dict),
             Self::Group(v) => v.eval(ctx, scp),
@@ -222,7 +222,7 @@ impl Eval for Expr {
             Self::While(v) => v.eval(ctx, scp),
             Self::For(v) => v.eval(ctx, scp),
             Self::Import(v) => v.eval(ctx, scp),
-            Self::Include(v) => v.eval(ctx, scp).map(Value::Template),
+            Self::Include(v) => v.eval(ctx, scp).map(Value::Content),
             Self::Break(v) => v.eval(ctx, scp),
             Self::Continue(v) => v.eval(ctx, scp),
             Self::Return(v) => v.eval(ctx, scp),
@@ -276,14 +276,14 @@ impl Eval for CodeBlock {
     }
 }
 
-impl Eval for TemplateBlock {
-    type Output = Template;
+impl Eval for ContentBlock {
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
         scp.enter();
-        let template = self.body().eval(ctx, scp)?;
+        let content = self.body().eval(ctx, scp)?;
         scp.exit();
-        Ok(template)
+        Ok(content)
     }
 }
 
@@ -716,13 +716,13 @@ impl Eval for ImportExpr {
 }
 
 impl Eval for IncludeExpr {
-    type Output = Template;
+    type Output = Content;
 
     fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
         let span = self.path().span();
         let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?;
         let module = import(ctx, &path, span)?;
-        Ok(module.template.clone())
+        Ok(module.content.clone())
     }
 }
 
diff --git a/src/eval/module.rs b/src/eval/module.rs
index 478c76b79..4460caece 100644
--- a/src/eval/module.rs
+++ b/src/eval/module.rs
@@ -1,4 +1,4 @@
-use super::{Scope, Template};
+use super::{Content, Scope};
 use crate::source::{SourceId, SourceStore};
 
 /// An evaluated module, ready for importing or layouting.
@@ -7,7 +7,7 @@ pub struct Module {
     /// The top-level definitions that were bound in this module.
     pub scope: Scope,
     /// The module's layoutable contents.
-    pub template: Template,
+    pub content: Content,
     /// The source file revisions this module depends on.
     pub deps: Vec<(SourceId, usize)>,
 }
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index acceb6267..04a13fd11 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -19,11 +19,11 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
         (a, None) => a,
         (None, b) => b,
         (Str(a), Str(b)) => Str(a + b),
+        (Str(a), Content(b)) => Content(super::Content::Text(a) + b),
+        (Content(a), Str(b)) => Content(a + super::Content::Text(b)),
+        (Content(a), Content(b)) => Content(a + b),
         (Array(a), Array(b)) => Array(a + b),
         (Dict(a), Dict(b)) => Dict(a + b),
-        (Template(a), Template(b)) => Template(a + b),
-        (Template(a), Str(b)) => Template(a + super::Template::Text(b)),
-        (Str(a), Template(b)) => Template(super::Template::Text(a) + b),
         (a, b) => mismatch!("cannot join {} with {}", a, b),
     })
 }
@@ -81,15 +81,16 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
         (Fractional(a), Fractional(b)) => Fractional(a + b),
 
         (Str(a), Str(b)) => Str(a + b),
+
+        (Content(a), None) => Content(a),
+        (None, Content(b)) => Content(b),
+        (Content(a), Content(b)) => Content(a + b),
+        (Content(a), Str(b)) => Content(a + super::Content::Text(b)),
+        (Str(a), Content(b)) => Content(super::Content::Text(a) + b),
+
         (Array(a), Array(b)) => Array(a + b),
         (Dict(a), Dict(b)) => Dict(a + b),
 
-        (Template(a), None) => Template(a),
-        (None, Template(b)) => Template(b),
-        (Template(a), Template(b)) => Template(a + b),
-        (Template(a), Str(b)) => Template(a + super::Template::Text(b)),
-        (Str(a), Template(b)) => Template(super::Template::Text(a) + b),
-
         (a, b) => {
             if let (Dyn(a), Dyn(b)) = (&a, &b) {
                 // 1D alignments can be summed into 2D alignments.
@@ -177,8 +178,8 @@ pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
         (Int(a), Str(b)) => Str(repeat_str(b, a)?),
         (Array(a), Int(b)) => Array(a.repeat(b)?),
         (Int(a), Array(b)) => Array(b.repeat(a)?),
-        (Template(a), Int(b)) => Template(a.repeat(b)?),
-        (Int(a), Template(b)) => Template(b.repeat(a)?),
+        (Content(a), Int(b)) => Content(a.repeat(b)?),
+        (Int(a), Content(b)) => Content(b.repeat(a)?),
 
         (a, b) => mismatch!("cannot multiply {} with {}", a, b),
     })
@@ -293,9 +294,9 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
         (Fractional(a), Fractional(b)) => a == b,
         (Color(a), Color(b)) => a == b,
         (Str(a), Str(b)) => a == b,
+        (Content(a), Content(b)) => a == b,
         (Array(a), Array(b)) => a == b,
         (Dict(a), Dict(b)) => a == b,
-        (Template(a), Template(b)) => a == b,
         (Func(a), Func(b)) => a == b,
         (Dyn(a), Dyn(b)) => a == b,
 
diff --git a/src/eval/show.rs b/src/eval/show.rs
index b0fb8172e..e85903d23 100644
--- a/src/eval/show.rs
+++ b/src/eval/show.rs
@@ -3,15 +3,15 @@ use std::fmt::{self, Debug, Formatter};
 use std::hash::Hash;
 use std::sync::Arc;
 
-use super::{StyleChain, Template};
+use super::{Content, StyleChain};
 use crate::diag::TypResult;
 use crate::util::Prehashed;
 use crate::Context;
 
 /// A node that can be realized given some styles.
 pub trait Show {
-    /// Realize the template in the given styles.
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template>;
+    /// Realize this node in the given styles.
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content>;
 
     /// Convert to a packed show node.
     fn pack(self) -> ShowNode
@@ -42,7 +42,7 @@ impl ShowNode {
 }
 
 impl Show for ShowNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         self.0.show(ctx, styles)
     }
 
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index 5a8371a96..20c36ae20 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
 use std::hash::{Hash, Hasher};
 use std::sync::Arc;
 
-use super::{Args, Func, Span, Template, Value};
+use super::{Args, Content, Func, Span, Value};
 use crate::diag::{At, TypResult};
 use crate::library::layout::PageNode;
 use crate::library::text::ParNode;
@@ -414,7 +414,7 @@ impl<'a> StyleChain<'a> {
         node: &dyn Any,
         ctx: &mut Context,
         values: impl IntoIterator<Item = Value>,
-    ) -> TypResult<Option<Template>> {
+    ) -> TypResult<Option<Content>> {
         Ok(if let Some(recipe) = self.recipes(node.type_id()).next() {
             let args = Args::from_values(recipe.span, values);
             Some(recipe.func.call(ctx, args)?.cast().at(recipe.span)?)
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 7d41bff50..48b2139f1 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
 use std::hash::{Hash, Hasher};
 use std::sync::Arc;
 
-use super::{ops, Args, Array, Class, Dict, Func, Layout, Template};
+use super::{ops, Args, Array, Class, Content, Dict, Func, Layout};
 use crate::diag::{with_alternative, StrResult};
 use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
 use crate::syntax::Spanned;
@@ -37,12 +37,12 @@ pub enum Value {
     Color(Color),
     /// A string: `"string"`.
     Str(EcoString),
+    /// A content value: `[*Hi* there]`.
+    Content(Content),
     /// An array of values: `(1, "hi", 12cm)`.
     Array(Array),
     /// A dictionary value: `(color: #f79143, pattern: dashed)`.
     Dict(Dict),
-    /// A template value: `[*Hi* there]`.
-    Template(Template),
     /// An executable function.
     Func(Func),
     /// Captured arguments to a function.
@@ -54,20 +54,20 @@ pub enum Value {
 }
 
 impl Value {
-    /// Create a template value from an inline-level node.
+    /// Create a content value from an inline-level node.
     pub fn inline<T>(node: T) -> Self
     where
         T: Layout + Debug + Hash + Sync + Send + 'static,
     {
-        Self::Template(Template::inline(node))
+        Self::Content(Content::inline(node))
     }
 
-    /// Create a template value from a block-level node.
+    /// Create a content value from a block-level node.
     pub fn block<T>(node: T) -> Self
     where
         T: Layout + Debug + Hash + Sync + Send + 'static,
     {
-        Self::Template(Template::block(node))
+        Self::Content(Content::block(node))
     }
 
     /// The name of the stored value's type.
@@ -85,9 +85,9 @@ impl Value {
             Self::Fractional(_) => Fractional::TYPE_NAME,
             Self::Color(_) => Color::TYPE_NAME,
             Self::Str(_) => EcoString::TYPE_NAME,
+            Self::Content(_) => Content::TYPE_NAME,
             Self::Array(_) => Array::TYPE_NAME,
             Self::Dict(_) => Dict::TYPE_NAME,
-            Self::Template(_) => Template::TYPE_NAME,
             Self::Func(_) => Func::TYPE_NAME,
             Self::Args(_) => Args::TYPE_NAME,
             Self::Class(_) => Class::TYPE_NAME,
@@ -111,16 +111,16 @@ impl Value {
     }
 
     /// Return the display representation of the value.
-    pub fn display(self) -> Template {
+    pub fn display(self) -> Content {
         match self {
-            Value::None => Template::new(),
-            Value::Int(v) => Template::Text(format_eco!("{}", v)),
-            Value::Float(v) => Template::Text(format_eco!("{}", v)),
-            Value::Str(v) => Template::Text(v),
-            Value::Template(v) => v,
+            Value::None => Content::new(),
+            Value::Int(v) => Content::Text(format_eco!("{}", v)),
+            Value::Float(v) => Content::Text(format_eco!("{}", v)),
+            Value::Str(v) => Content::Text(v),
+            Value::Content(v) => v,
             // For values which can't be shown "naturally", we print the
             // representation in monospace.
-            v => Template::Text(v.repr()).monospaced(),
+            v => Content::Text(v.repr()).monospaced(),
         }
     }
 }
@@ -146,9 +146,9 @@ impl Debug for Value {
             Self::Fractional(v) => Debug::fmt(v, f),
             Self::Color(v) => Debug::fmt(v, f),
             Self::Str(v) => Debug::fmt(v, f),
+            Self::Content(_) => f.pad("<content>"),
             Self::Array(v) => Debug::fmt(v, f),
             Self::Dict(v) => Debug::fmt(v, f),
-            Self::Template(_) => f.pad("<template>"),
             Self::Func(v) => Debug::fmt(v, f),
             Self::Args(v) => Debug::fmt(v, f),
             Self::Class(v) => Debug::fmt(v, f),
@@ -185,9 +185,9 @@ impl Hash for Value {
             Self::Fractional(v) => v.hash(state),
             Self::Color(v) => v.hash(state),
             Self::Str(v) => v.hash(state),
+            Self::Content(v) => v.hash(state),
             Self::Array(v) => v.hash(state),
             Self::Dict(v) => v.hash(state),
-            Self::Template(v) => v.hash(state),
             Self::Func(v) => v.hash(state),
             Self::Args(v) => v.hash(state),
             Self::Class(v) => v.hash(state),
@@ -441,9 +441,9 @@ primitive! { Linear: "relative length", Linear, Length(v) => v.into(), Relative(
 primitive! { Fractional: "fractional length", Fractional }
 primitive! { Color: "color", Color }
 primitive! { EcoString: "string", Str }
+primitive! { Content: "content", Content, None => Content::new() }
 primitive! { Array: "array", Array }
 primitive! { Dict: "dictionary", Dict }
-primitive! { Template: "template", Template, None => Template::new() }
 primitive! { Func: "function", Func, Class(v) => v.constructor() }
 primitive! { Args: "arguments", Args }
 primitive! { Class: "class", Class }
diff --git a/src/lib.rs b/src/lib.rs
index 8ef538c58..cb434e62a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,7 +7,7 @@
 //!   provided in the [AST] module.
 //! - **Evaluation:** The next step is to [evaluate] the markup. This produces a
 //!   [module], consisting of a scope of values that were exported by the code
-//!   and a [template], a hierarchical, styled representation with the contents
+//!   and [content], a hierarchical, styled representation with the contents
 //!   of the module. The nodes of this tree are well structured and
 //!   order-independent and thus much better suited for layouting than the raw
 //!   markup.
@@ -23,8 +23,8 @@
 //! [AST]: syntax::ast
 //! [evaluate]: eval::Eval
 //! [module]: eval::Module
-//! [template]: eval::Template
-//! [layouted]: eval::Template::layout
+//! [content]: eval::Content
+//! [layouted]: eval::Content::layout
 //! [PDF]: export::pdf
 
 #![allow(clippy::len_without_is_empty)]
@@ -110,8 +110,8 @@ impl Context {
 
     /// Evaluate a source file and return the resulting module.
     ///
-    /// Returns either a module containing a scope with top-level bindings and a
-    /// layoutable template or diagnostics in the form of a vector of error
+    /// Returns either a module containing a scope with top-level bindings and
+    /// layoutable contents or diagnostics in the form of a vector of error
     /// messages with file and span information.
     pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> {
         // Prevent cyclic evaluation.
@@ -139,16 +139,12 @@ impl Context {
         // Evaluate the module.
         let prev = std::mem::replace(&mut self.deps, vec![(id, source.rev())]);
         self.route.push(id);
-        let template = ast.eval(self, &mut scp);
+        let content = ast.eval(self, &mut scp);
         self.route.pop().unwrap();
         let deps = std::mem::replace(&mut self.deps, prev);
 
         // Assemble the module.
-        let module = Module {
-            scope: scp.top,
-            template: template?,
-            deps,
-        };
+        let module = Module { scope: scp.top, content: content?, deps };
 
         // Save the evaluated module.
         self.modules.insert(id, module.clone());
@@ -162,7 +158,7 @@ impl Context {
     /// diagnostics in the form of a vector of error message with file and span
     /// information.
     pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> {
-        self.evaluate(id)?.template.layout(self)
+        self.evaluate(id)?.content.layout(self)
     }
 
     /// Resolve a user-entered path (relative to the current evaluation
diff --git a/src/library/graphics/hide.rs b/src/library/graphics/hide.rs
index 861a12083..21fc58c25 100644
--- a/src/library/graphics/hide.rs
+++ b/src/library/graphics/hide.rs
@@ -6,8 +6,8 @@ pub struct HideNode(pub LayoutNode);
 
 #[class]
 impl HideNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::inline(Self(args.expect("body")?)))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::inline(Self(args.expect("body")?)))
     }
 }
 
diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs
index 66fb8f4b2..faf89850f 100644
--- a/src/library/graphics/image.rs
+++ b/src/library/graphics/image.rs
@@ -12,7 +12,7 @@ impl ImageNode {
     /// How the image should adjust itself to a given area.
     pub const FIT: ImageFit = ImageFit::Cover;
 
-    fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content> {
         let path = args.expect::<Spanned<EcoString>>("path to image file")?;
         let full = ctx.resolve(&path.v);
         let id = ctx.images.load(&full).map_err(|err| {
@@ -25,7 +25,7 @@ impl ImageNode {
         let width = args.named("width")?;
         let height = args.named("height")?;
 
-        Ok(Template::inline(
+        Ok(Content::inline(
             ImageNode(id).pack().sized(Spec::new(width, height)),
         ))
     }
diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs
index 8b967412a..fbe213479 100644
--- a/src/library/graphics/shape.rs
+++ b/src/library/graphics/shape.rs
@@ -30,7 +30,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
     /// How much to pad the shape's content.
     pub const PADDING: Linear = Linear::zero();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let size = match S {
             SQUARE => args.named::<Length>("size")?.map(Linear::from),
             CIRCLE => args.named::<Length>("radius")?.map(|r| 2.0 * Linear::from(r)),
@@ -47,7 +47,7 @@ impl<const S: ShapeKind> ShapeNode<S> {
             size => size,
         };
 
-        Ok(Template::inline(
+        Ok(Content::inline(
             Self(args.find()?).pack().sized(Spec::new(width, height)),
         ))
     }
diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs
index fafb37a4a..fcd7528d4 100644
--- a/src/library/graphics/transform.rs
+++ b/src/library/graphics/transform.rs
@@ -24,7 +24,7 @@ impl<const T: TransformKind> TransformNode<T> {
     /// The origin of the transformation.
     pub const ORIGIN: Spec<Option<Align>> = Spec::default();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let transform = match T {
             MOVE => {
                 let tx = args.named("x")?.unwrap_or_default();
@@ -43,7 +43,7 @@ impl<const T: TransformKind> TransformNode<T> {
             }
         };
 
-        Ok(Template::inline(Self {
+        Ok(Content::inline(Self {
             transform,
             child: args.expect("body")?,
         }))
diff --git a/src/library/layout/align.rs b/src/library/layout/align.rs
index 7fbe0d015..2a9695248 100644
--- a/src/library/layout/align.rs
+++ b/src/library/layout/align.rs
@@ -12,10 +12,10 @@ pub struct AlignNode {
 
 #[class]
 impl AlignNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let aligns: Spec<_> = args.find()?.unwrap_or_default();
         let body: LayoutNode = args.expect("body")?;
-        Ok(Template::block(body.aligned(aligns)))
+        Ok(Content::block(body.aligned(aligns)))
     }
 }
 
diff --git a/src/library/layout/columns.rs b/src/library/layout/columns.rs
index 167e70682..9e461108d 100644
--- a/src/library/layout/columns.rs
+++ b/src/library/layout/columns.rs
@@ -16,8 +16,8 @@ impl ColumnsNode {
     /// The size of the gutter space between each column.
     pub const GUTTER: Linear = Relative::new(0.04).into();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::block(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::block(Self {
             columns: args.expect("column count")?,
             child: args.expect("body")?,
         }))
@@ -105,7 +105,7 @@ pub struct ColbreakNode;
 
 #[class]
 impl ColbreakNode {
-    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> {
-        Ok(Template::Colbreak)
+    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
+        Ok(Content::Colbreak)
     }
 }
diff --git a/src/library/layout/container.rs b/src/library/layout/container.rs
index 555798789..f7f4017c3 100644
--- a/src/library/layout/container.rs
+++ b/src/library/layout/container.rs
@@ -5,11 +5,11 @@ pub struct BoxNode;
 
 #[class]
 impl BoxNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let width = args.named("width")?;
         let height = args.named("height")?;
         let body: LayoutNode = args.find()?.unwrap_or_default();
-        Ok(Template::inline(body.sized(Spec::new(width, height))))
+        Ok(Content::inline(body.sized(Spec::new(width, height))))
     }
 }
 
@@ -18,7 +18,7 @@ pub struct BlockNode;
 
 #[class]
 impl BlockNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::Block(args.find()?.unwrap_or_default()))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::Block(args.find()?.unwrap_or_default()))
     }
 }
diff --git a/src/library/layout/grid.rs b/src/library/layout/grid.rs
index 63cd83b1a..90cf6da3a 100644
--- a/src/library/layout/grid.rs
+++ b/src/library/layout/grid.rs
@@ -13,13 +13,13 @@ pub struct GridNode {
 
 #[class]
 impl GridNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let columns = args.named("columns")?.unwrap_or_default();
         let rows = args.named("rows")?.unwrap_or_default();
         let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
         let column_gutter = args.named("column-gutter")?;
         let row_gutter = args.named("row-gutter")?;
-        Ok(Template::block(Self {
+        Ok(Content::block(Self {
             tracks: Spec::new(columns, rows),
             gutter: Spec::new(
                 column_gutter.unwrap_or_else(|| base_gutter.clone()),
diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs
index 175a54f03..835beef98 100644
--- a/src/library/layout/pad.rs
+++ b/src/library/layout/pad.rs
@@ -11,7 +11,7 @@ pub struct PadNode {
 
 #[class]
 impl PadNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let all = args.find()?;
         let hor = args.named("horizontal")?;
         let ver = args.named("vertical")?;
@@ -21,7 +21,7 @@ impl PadNode {
         let bottom = args.named("bottom")?.or(ver).or(all).unwrap_or_default();
         let body: LayoutNode = args.expect("body")?;
         let padding = Sides::new(left, top, right, bottom);
-        Ok(Template::block(body.padded(padding)))
+        Ok(Content::block(body.padded(padding)))
     }
 }
 
diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs
index 4b6d68c52..def2940e9 100644
--- a/src/library/layout/page.rs
+++ b/src/library/layout/page.rs
@@ -32,8 +32,8 @@ impl PageNode {
     /// The page's footer.
     pub const FOOTER: Marginal = Marginal::None;
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::Page(Self(args.expect("body")?)))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::Page(Self(args.expect("body")?)))
     }
 
     fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
@@ -125,12 +125,12 @@ impl PageNode {
                 (Length::zero(), padding.top, header),
                 (size.y - padding.bottom, padding.bottom, footer),
             ] {
-                if let Some(template) = marginal.resolve(ctx, page)? {
+                if let Some(content) = marginal.resolve(ctx, page)? {
                     let pos = Point::new(padding.left, y);
                     let w = size.x - padding.left - padding.right;
                     let area = Size::new(w, h);
                     let pod = Regions::one(area, area, area.map(Length::is_finite));
-                    let sub = Layout::layout(&template, ctx, &pod, styles)?.remove(0);
+                    let sub = Layout::layout(&content, ctx, &pod, styles)?.remove(0);
                     Arc::make_mut(frame).push_frame(pos, sub);
                 }
             }
@@ -155,8 +155,8 @@ pub struct PagebreakNode;
 
 #[class]
 impl PagebreakNode {
-    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> {
-        Ok(Template::Pagebreak)
+    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
+        Ok(Content::Pagebreak)
     }
 }
 
@@ -165,18 +165,18 @@ impl PagebreakNode {
 pub enum Marginal {
     /// Nothing,
     None,
-    /// A bare template.
-    Template(Template),
-    /// A closure mapping from a page number to a template.
+    /// Bare content.
+    Content(Content),
+    /// A closure mapping from a page number to content.
     Func(Func, Span),
 }
 
 impl Marginal {
     /// Resolve the marginal based on the page number.
-    pub fn resolve(&self, ctx: &mut Context, page: usize) -> TypResult<Option<Template>> {
+    pub fn resolve(&self, ctx: &mut Context, page: usize) -> TypResult<Option<Content>> {
         Ok(match self {
             Self::None => None,
-            Self::Template(template) => Some(template.clone()),
+            Self::Content(content) => Some(content.clone()),
             Self::Func(func, span) => {
                 let args = Args::from_values(*span, [Value::Int(page as i64)]);
                 func.call(ctx, args)?.cast().at(*span)?
@@ -187,15 +187,15 @@ impl Marginal {
 
 impl Cast<Spanned<Value>> for Marginal {
     fn is(value: &Spanned<Value>) -> bool {
-        matches!(&value.v, Value::Template(_) | Value::Func(_))
+        matches!(&value.v, Value::Content(_) | Value::Func(_))
     }
 
     fn cast(value: Spanned<Value>) -> StrResult<Self> {
         match value.v {
             Value::None => Ok(Self::None),
-            Value::Template(v) => Ok(Self::Template(v)),
+            Value::Content(v) => Ok(Self::Content(v)),
             Value::Func(v) => Ok(Self::Func(v, value.span)),
-            _ => Err("expected none, template or function")?,
+            _ => Err("expected none, content or function")?,
         }
     }
 }
diff --git a/src/library/layout/place.rs b/src/library/layout/place.rs
index d65b38361..99ff52927 100644
--- a/src/library/layout/place.rs
+++ b/src/library/layout/place.rs
@@ -7,12 +7,12 @@ pub struct PlaceNode(pub LayoutNode);
 
 #[class]
 impl PlaceNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let aligns = args.find()?.unwrap_or(Spec::with_x(Some(Align::Left)));
         let tx = args.named("dx")?.unwrap_or_default();
         let ty = args.named("dy")?.unwrap_or_default();
         let body: LayoutNode = args.expect("body")?;
-        Ok(Template::block(Self(
+        Ok(Content::block(Self(
             body.moved(Point::new(tx, ty)).aligned(aligns),
         )))
     }
diff --git a/src/library/layout/spacing.rs b/src/library/layout/spacing.rs
index 3bebfb14d..9a27a8b2e 100644
--- a/src/library/layout/spacing.rs
+++ b/src/library/layout/spacing.rs
@@ -5,8 +5,8 @@ pub struct HNode;
 
 #[class]
 impl HNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::Horizontal(args.expect("spacing")?))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::Horizontal(args.expect("spacing")?))
     }
 }
 
@@ -15,8 +15,8 @@ pub struct VNode;
 
 #[class]
 impl VNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::Vertical(args.expect("spacing")?))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::Vertical(args.expect("spacing")?))
     }
 }
 
diff --git a/src/library/layout/stack.rs b/src/library/layout/stack.rs
index 414490ef8..88e271168 100644
--- a/src/library/layout/stack.rs
+++ b/src/library/layout/stack.rs
@@ -14,8 +14,8 @@ pub struct StackNode {
 
 #[class]
 impl StackNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::block(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::block(Self {
             dir: args.named("dir")?.unwrap_or(Dir::TTB),
             spacing: args.named("spacing")?,
             children: args.all()?,
@@ -76,12 +76,12 @@ impl Debug for StackChild {
 
 castable! {
     StackChild,
-    Expected: "linear, fractional or template",
+    Expected: "linear, fractional or content",
     Value::Length(v) => Self::Spacing(SpacingKind::Linear(v.into())),
     Value::Relative(v) => Self::Spacing(SpacingKind::Linear(v.into())),
     Value::Linear(v) => Self::Spacing(SpacingKind::Linear(v)),
     Value::Fractional(v) => Self::Spacing(SpacingKind::Fractional(v)),
-    Value::Template(v) => Self::Node(v.pack()),
+    Value::Content(v) => Self::Node(v.pack()),
 }
 
 /// Performs stack layout.
diff --git a/src/library/math/mod.rs b/src/library/math/mod.rs
index c5b791171..b43db22e1 100644
--- a/src/library/math/mod.rs
+++ b/src/library/math/mod.rs
@@ -13,8 +13,8 @@ pub struct MathNode {
 
 #[class]
 impl MathNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self {
             formula: args.expect("formula")?,
             display: args.named("display")?.unwrap_or(false),
         }))
@@ -22,18 +22,18 @@ impl MathNode {
 }
 
 impl Show for MathNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         Ok(styles
             .show(self, ctx, [
                 Value::Str(self.formula.clone()),
                 Value::Bool(self.display),
             ])?
             .unwrap_or_else(|| {
-                let mut template = Template::Text(self.formula.trim().into());
+                let mut content = Content::Text(self.formula.trim().into());
                 if self.display {
-                    template = Template::Block(template.pack());
+                    content = Content::Block(content.pack());
                 }
-                template.monospaced()
+                content.monospaced()
             }))
     }
 }
diff --git a/src/library/mod.rs b/src/library/mod.rs
index ceb5a5571..b2e4e4087 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -167,6 +167,6 @@ castable! {
 
 castable! {
     LayoutNode,
-    Expected: "template",
-    Value::Template(template) => template.pack(),
+    Expected: "content",
+    Value::Content(content) => content.pack(),
 }
diff --git a/src/library/prelude.rs b/src/library/prelude.rs
index 4d13a655f..0cca718f1 100644
--- a/src/library/prelude.rs
+++ b/src/library/prelude.rs
@@ -9,8 +9,8 @@ pub use typst_macros::class;
 
 pub use crate::diag::{with_alternative, At, StrResult, TypResult};
 pub use crate::eval::{
-    Arg, Args, Array, Cast, Construct, Dict, Func, Layout, LayoutNode, Merge, Property,
-    Regions, Scope, Set, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec, Template,
+    Arg, Args, Array, Cast, Construct, Content, Dict, Func, Layout, LayoutNode, Merge,
+    Property, Regions, Scope, Set, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec,
     Value,
 };
 pub use crate::frame::*;
diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs
index a67f4f245..f5565f3c9 100644
--- a/src/library/structure/heading.rs
+++ b/src/library/structure/heading.rs
@@ -8,7 +8,7 @@ pub struct HeadingNode {
     /// default style, this controls the text size of the heading.
     pub level: usize,
     /// The heading's contents.
-    pub body: Template,
+    pub body: Content,
 }
 
 #[class]
@@ -35,8 +35,8 @@ impl HeadingNode {
     /// Whether the heading is block-level.
     pub const BLOCK: Leveled<bool> = Leveled::Value(true);
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self {
             body: args.expect("body")?,
             level: args.named("level")?.unwrap_or(1),
         }))
@@ -44,7 +44,7 @@ impl HeadingNode {
 }
 
 impl Show for HeadingNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         macro_rules! resolve {
             ($key:expr) => {
                 styles.get_cloned($key).resolve(ctx, self.level)?
@@ -55,7 +55,7 @@ impl Show for HeadingNode {
         let mut body = styles
             .show(self, ctx, [
                 Value::Int(self.level as i64),
-                Value::Template(self.body.clone()),
+                Value::Content(self.body.clone()),
             ])?
             .unwrap_or_else(|| self.body.clone());
 
@@ -90,22 +90,22 @@ impl Show for HeadingNode {
 
         let above = resolve!(Self::ABOVE);
         if !above.is_zero() {
-            seq.push(Template::Vertical(above.into()));
+            seq.push(Content::Vertical(above.into()));
         }
 
         seq.push(body);
 
         let below = resolve!(Self::BELOW);
         if !below.is_zero() {
-            seq.push(Template::Vertical(below.into()));
+            seq.push(Content::Vertical(below.into()));
         }
 
-        let mut template = Template::sequence(seq).styled_with_map(map);
+        let mut content = Content::sequence(seq).styled_with_map(map);
         if resolve!(Self::BLOCK) {
-            template = Template::block(template);
+            content = Content::block(content);
         }
 
-        Ok(template)
+        Ok(content)
     }
 }
 
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
index 2c536e2af..2630d231b 100644
--- a/src/library/structure/list.rs
+++ b/src/library/structure/list.rs
@@ -22,7 +22,7 @@ pub struct ListItem {
     /// The number of the item.
     pub number: Option<usize>,
     /// The node that produces the item's body.
-    pub body: Box<Template>,
+    pub body: Box<Content>,
 }
 
 /// An ordered list.
@@ -43,8 +43,8 @@ impl<const L: ListKind> ListNode<L> {
     /// The extra padding below the list.
     pub const BELOW: Length = Length::zero();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self {
             start: args.named("start")?.unwrap_or(0),
             wide: args.named("wide")?.unwrap_or(false),
             items: args
@@ -57,13 +57,13 @@ impl<const L: ListKind> ListNode<L> {
 }
 
 impl<const L: ListKind> Show for ListNode<L> {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
-        let template = if let Some(template) = styles.show(
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
+        let content = if let Some(content) = styles.show(
             self,
             ctx,
-            self.items.iter().map(|item| Value::Template((*item.body).clone())),
+            self.items.iter().map(|item| Value::Content((*item.body).clone())),
         )? {
-            template
+            content
         } else {
             let mut children = vec![];
             let mut number = self.start;
@@ -91,7 +91,7 @@ impl<const L: ListKind> Show for ListNode<L> {
             let indent = styles.get(Self::INDENT).resolve(em);
             let body_indent = styles.get(Self::BODY_INDENT).resolve(em);
 
-            Template::block(GridNode {
+            Content::block(GridNode {
                 tracks: Spec::with_x(vec![
                     TrackSizing::Linear(indent.into()),
                     TrackSizing::Auto,
@@ -106,17 +106,17 @@ impl<const L: ListKind> Show for ListNode<L> {
         let mut seq = vec![];
         let above = styles.get(Self::ABOVE);
         if !above.is_zero() {
-            seq.push(Template::Vertical(above.into()));
+            seq.push(Content::Vertical(above.into()));
         }
 
-        seq.push(template);
+        seq.push(content);
 
         let below = styles.get(Self::BELOW);
         if !below.is_zero() {
-            seq.push(Template::Vertical(below.into()));
+            seq.push(Content::Vertical(below.into()));
         }
 
-        Ok(Template::sequence(seq))
+        Ok(Content::sequence(seq))
     }
 }
 
@@ -135,15 +135,15 @@ pub const UNORDERED: ListKind = 0;
 /// Ordered list labelling style.
 pub const ORDERED: ListKind = 1;
 
-/// Either a template or a closure mapping to a template.
+/// Either content or a closure mapping to content.
 #[derive(Debug, Clone, PartialEq, Hash)]
 pub enum Label {
     /// The default labelling.
     Default,
     /// A pattern with prefix, numbering, lower / upper case and suffix.
     Pattern(EcoString, Numbering, bool, EcoString),
-    /// A bare template.
-    Template(Template),
+    /// Bare content.
+    Content(Content),
     /// A closure mapping from an item number to a value.
     Func(Func, Span),
 }
@@ -155,18 +155,18 @@ impl Label {
         ctx: &mut Context,
         kind: ListKind,
         number: usize,
-    ) -> TypResult<Template> {
+    ) -> TypResult<Content> {
         Ok(match self {
             Self::Default => match kind {
-                UNORDERED => Template::Text('•'.into()),
-                ORDERED | _ => Template::Text(format_eco!("{}.", number)),
+                UNORDERED => Content::Text('•'.into()),
+                ORDERED | _ => Content::Text(format_eco!("{}.", number)),
             },
             Self::Pattern(prefix, numbering, upper, suffix) => {
                 let fmt = numbering.apply(number);
                 let mid = if *upper { fmt.to_uppercase() } else { fmt.to_lowercase() };
-                Template::Text(format_eco!("{}{}{}", prefix, mid, suffix))
+                Content::Text(format_eco!("{}{}{}", prefix, mid, suffix))
             }
-            Self::Template(template) => template.clone(),
+            Self::Content(content) => content.clone(),
             Self::Func(func, span) => {
                 let args = Args::from_values(*span, [Value::Int(number as i64)]);
                 func.call(ctx, args)?.cast().at(*span)?
@@ -177,7 +177,7 @@ impl Label {
 
 impl Cast<Spanned<Value>> for Label {
     fn is(value: &Spanned<Value>) -> bool {
-        matches!(&value.v, Value::Template(_) | Value::Func(_))
+        matches!(&value.v, Value::Content(_) | Value::Func(_))
     }
 
     fn cast(value: Spanned<Value>) -> StrResult<Self> {
@@ -200,9 +200,9 @@ impl Cast<Spanned<Value>> for Label {
                 let suffix = s.rest().into();
                 Ok(Self::Pattern(prefix.into(), numbering, upper, suffix))
             }
-            Value::Template(v) => Ok(Self::Template(v)),
+            Value::Content(v) => Ok(Self::Content(v)),
             Value::Func(v) => Ok(Self::Func(v, value.span)),
-            _ => Err("expected pattern, template or function")?,
+            _ => Err("expected pattern, content or function")?,
         }
     }
 }
diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs
index 555dcc449..42b62eac7 100644
--- a/src/library/structure/table.rs
+++ b/src/library/structure/table.rs
@@ -9,7 +9,7 @@ pub struct TableNode {
     /// Defines sizing of gutter rows and columns between content.
     pub gutter: Spec<Vec<TrackSizing>>,
     /// The nodes to be arranged in the table.
-    pub children: Vec<Template>,
+    pub children: Vec<Content>,
 }
 
 #[class]
@@ -25,13 +25,13 @@ impl TableNode {
     /// How much to pad the cells's content.
     pub const PADDING: Linear = Length::pt(5.0).into();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         let columns = args.named("columns")?.unwrap_or_default();
         let rows = args.named("rows")?.unwrap_or_default();
         let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
         let column_gutter = args.named("column-gutter")?;
         let row_gutter = args.named("row-gutter")?;
-        Ok(Template::show(Self {
+        Ok(Content::show(Self {
             tracks: Spec::new(columns, rows),
             gutter: Spec::new(
                 column_gutter.unwrap_or_else(|| base_gutter.clone()),
@@ -53,13 +53,13 @@ impl TableNode {
 }
 
 impl Show for TableNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
-        if let Some(template) = styles.show(
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
+        if let Some(content) = styles.show(
             self,
             ctx,
-            self.children.iter().map(|child| Value::Template(child.clone())),
+            self.children.iter().map(|child| Value::Content(child.clone())),
         )? {
-            return Ok(template);
+            return Ok(content);
         }
 
         let primary = styles.get(Self::PRIMARY);
@@ -91,7 +91,7 @@ impl Show for TableNode {
             })
             .collect();
 
-        Ok(Template::block(GridNode {
+        Ok(Content::block(GridNode {
             tracks: self.tracks.clone(),
             gutter: self.gutter.clone(),
             children,
diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs
index a288c9956..608ebb185 100644
--- a/src/library/text/deco.rs
+++ b/src/library/text/deco.rs
@@ -7,7 +7,7 @@ use crate::library::prelude::*;
 
 /// Typeset underline, stricken-through or overlined text.
 #[derive(Debug, Hash)]
-pub struct DecoNode<const L: DecoLine>(pub Template);
+pub struct DecoNode<const L: DecoLine>(pub Content);
 
 /// Typeset underlined text.
 pub type UnderlineNode = DecoNode<UNDERLINE>;
@@ -37,15 +37,15 @@ impl<const L: DecoLine> DecoNode<L> {
     /// with the glyphs. Does not apply to strikethrough.
     pub const EVADE: bool = true;
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self(args.expect::<Template>("body")?)))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self(args.expect::<Content>("body")?)))
     }
 }
 
 impl<const L: DecoLine> Show for DecoNode<L> {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         Ok(styles
-            .show(self, ctx, [Value::Template(self.0.clone())])?
+            .show(self, ctx, [Value::Content(self.0.clone())])?
             .unwrap_or_else(|| {
                 self.0.clone().styled(TextNode::LINES, vec![Decoration {
                     line: L,
diff --git a/src/library/text/link.rs b/src/library/text/link.rs
index 29f41927d..e0041df59 100644
--- a/src/library/text/link.rs
+++ b/src/library/text/link.rs
@@ -8,7 +8,7 @@ pub struct LinkNode {
     /// The url the link points to.
     pub url: EcoString,
     /// How the link is represented.
-    pub body: Option<Template>,
+    pub body: Option<Content>,
 }
 
 #[class]
@@ -19,8 +19,8 @@ impl LinkNode {
     /// Whether to underline link.
     pub const UNDERLINE: bool = true;
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self {
             url: args.expect::<EcoString>("url")?,
             body: args.find()?,
         }))
@@ -28,12 +28,12 @@ impl LinkNode {
 }
 
 impl Show for LinkNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         let mut body = styles
             .show(self, ctx, [
                 Value::Str(self.url.clone()),
                 match &self.body {
-                    Some(body) => Value::Template(body.clone()),
+                    Some(body) => Value::Content(body.clone()),
                     None => Value::None,
                 },
             ])?
@@ -45,7 +45,7 @@ impl Show for LinkNode {
                     text = text.trim_start_matches(prefix);
                 }
                 let shorter = text.len() < url.len();
-                Template::Text(if shorter { text.into() } else { url.clone() })
+                Content::Text(if shorter { text.into() } else { url.clone() })
             });
 
         let mut map = StyleMap::new();
diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs
index 197971d02..dbc486fba 100644
--- a/src/library/text/mod.rs
+++ b/src/library/text/mod.rs
@@ -110,7 +110,7 @@ impl TextNode {
     #[skip]
     pub const LINK: Option<EcoString> = None;
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         // The text constructor is special: It doesn't create a text node.
         // Instead, it leaves the passed argument structurally unchanged, but
         // styles all text in it.
@@ -120,38 +120,38 @@ impl TextNode {
 
 /// Strong text, rendered in boldface.
 #[derive(Debug, Hash)]
-pub struct StrongNode(pub Template);
+pub struct StrongNode(pub Content);
 
 #[class]
 impl StrongNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self(args.expect("body")?)))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self(args.expect("body")?)))
     }
 }
 
 impl Show for StrongNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         Ok(styles
-            .show(self, ctx, [Value::Template(self.0.clone())])?
+            .show(self, ctx, [Value::Content(self.0.clone())])?
             .unwrap_or_else(|| self.0.clone().styled(TextNode::STRONG, true)))
     }
 }
 
 /// Emphasized text, rendered with an italic face.
 #[derive(Debug, Hash)]
-pub struct EmphNode(pub Template);
+pub struct EmphNode(pub Content);
 
 #[class]
 impl EmphNode {
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self(args.expect("body")?)))
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self(args.expect("body")?)))
     }
 }
 
 impl Show for EmphNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         Ok(styles
-            .show(self, ctx, [Value::Template(self.0.clone())])?
+            .show(self, ctx, [Value::Content(self.0.clone())])?
             .unwrap_or_else(|| self.0.clone().styled(TextNode::EMPH, true)))
     }
 }
diff --git a/src/library/text/par.rs b/src/library/text/par.rs
index 70cac1be5..dc888637b 100644
--- a/src/library/text/par.rs
+++ b/src/library/text/par.rs
@@ -45,12 +45,12 @@ impl ParNode {
     /// The indent the first line of a consecutive paragraph should have.
     pub const INDENT: Linear = Linear::zero();
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
         // The paragraph constructor is special: It doesn't create a paragraph
         // since that happens automatically through markup. Instead, it just
         // lifts the passed body to the block level so that it won't merge with
         // adjacent stuff and it styles the contained paragraphs.
-        Ok(Template::Block(args.expect("body")?))
+        Ok(Content::Block(args.expect("body")?))
     }
 
     fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
@@ -185,8 +185,8 @@ pub struct ParbreakNode;
 
 #[class]
 impl ParbreakNode {
-    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> {
-        Ok(Template::Parbreak)
+    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
+        Ok(Content::Parbreak)
     }
 }
 
@@ -195,8 +195,8 @@ pub struct LinebreakNode;
 
 #[class]
 impl LinebreakNode {
-    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Template> {
-        Ok(Template::Linebreak)
+    fn construct(_: &mut Context, _: &mut Args) -> TypResult<Content> {
+        Ok(Content::Linebreak)
     }
 }
 
diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs
index 97857f119..988bd04e8 100644
--- a/src/library/text/raw.rs
+++ b/src/library/text/raw.rs
@@ -29,8 +29,8 @@ impl RawNode {
     /// The language to syntax-highlight in.
     pub const LANG: Option<EcoString> = None;
 
-    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Template> {
-        Ok(Template::show(Self {
+    fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
+        Ok(Content::show(Self {
             text: args.expect("text")?,
             block: args.named("block")?.unwrap_or(false),
         }))
@@ -38,10 +38,10 @@ impl RawNode {
 }
 
 impl Show for RawNode {
-    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Template> {
+    fn show(&self, ctx: &mut Context, styles: StyleChain) -> TypResult<Content> {
         let lang = styles.get_ref(Self::LANG).as_ref();
 
-        if let Some(template) = styles.show(self, ctx, [
+        if let Some(content) = styles.show(self, ctx, [
             Value::Str(self.text.clone()),
             match lang {
                 Some(lang) => Value::Str(lang.clone()),
@@ -49,7 +49,7 @@ impl Show for RawNode {
             },
             Value::Bool(self.block),
         ])? {
-            return Ok(template);
+            return Ok(content);
         }
 
         let foreground = THEME
@@ -59,7 +59,7 @@ impl Show for RawNode {
             .unwrap_or(Color::BLACK)
             .into();
 
-        let mut template = if matches!(
+        let mut content = if matches!(
             lang.map(|s| s.to_lowercase()).as_deref(),
             Some("typ" | "typst")
         ) {
@@ -72,7 +72,7 @@ impl Show for RawNode {
                 seq.push(styled(&self.text[range], foreground, style));
             });
 
-            Template::sequence(seq)
+            Content::sequence(seq)
         } else if let Some(syntax) =
             lang.and_then(|token| SYNTAXES.find_syntax_by_token(&token))
         {
@@ -80,7 +80,7 @@ impl Show for RawNode {
             let mut highlighter = HighlightLines::new(syntax, &THEME);
             for (i, line) in self.text.lines().enumerate() {
                 if i != 0 {
-                    seq.push(Template::Linebreak);
+                    seq.push(Content::Linebreak);
                 }
 
                 for (style, piece) in highlighter.highlight(line, &SYNTAXES) {
@@ -88,23 +88,23 @@ impl Show for RawNode {
                 }
             }
 
-            Template::sequence(seq)
+            Content::sequence(seq)
         } else {
-            Template::Text(self.text.clone())
+            Content::Text(self.text.clone())
         };
 
         if self.block {
-            template = Template::Block(template.pack());
+            content = Content::Block(content.pack());
         }
 
-        Ok(template.monospaced())
+        Ok(content.monospaced())
     }
 }
 
 /// Style a piece of text with a syntect style.
-fn styled(piece: &str, foreground: Paint, style: Style) -> Template {
+fn styled(piece: &str, foreground: Paint, style: Style) -> Content {
     let mut styles = StyleMap::new();
-    let mut body = Template::Text(piece.into());
+    let mut body = Content::Text(piece.into());
 
     let paint = style.foreground.into();
     if paint != foreground {
diff --git a/src/library/utility/math.rs b/src/library/utility/math.rs
index 795f39fd0..9389b4b95 100644
--- a/src/library/utility/math.rs
+++ b/src/library/utility/math.rs
@@ -39,7 +39,7 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
             }
             None => bail!(
                 span,
-                "cannot compare {} with {}",
+                "cannot compare {} and {}",
                 extremum.type_name(),
                 v.type_name(),
             ),
diff --git a/src/library/utility/mod.rs b/src/library/utility/mod.rs
index 886cdc138..d85c3f125 100644
--- a/src/library/utility/mod.rs
+++ b/src/library/utility/mod.rs
@@ -170,13 +170,13 @@ pub fn upper(_: &mut Context, args: &mut Args) -> TypResult<Value> {
     case(Case::Upper, args)
 }
 
-/// Change the case of a string or template.
+/// Change the case of a string or content.
 fn case(case: Case, args: &mut Args) -> TypResult<Value> {
-    let Spanned { v, span } = args.expect("string or template")?;
+    let Spanned { v, span } = args.expect("string or content")?;
     Ok(match v {
         Value::Str(v) => Value::Str(case.apply(&v).into()),
-        Value::Template(v) => Value::Template(v.styled(TextNode::CASE, Some(case))),
-        v => bail!(span, "expected string or template, found {}", v.type_name()),
+        Value::Content(v) => Value::Content(v.styled(TextNode::CASE, Some(case))),
+        v => bail!(span, "expected string or content, found {}", v.type_name()),
     })
 }
 
diff --git a/src/parse/incremental.rs b/src/parse/incremental.rs
index 759b3a68c..468f344e7 100644
--- a/src/parse/incremental.rs
+++ b/src/parse/incremental.rs
@@ -4,8 +4,7 @@ use std::sync::Arc;
 use crate::syntax::{Green, GreenNode, NodeKind};
 
 use super::{
-    is_newline, parse, reparse_block, reparse_markup_elements, reparse_template,
-    TokenMode,
+    is_newline, parse, reparse_block, reparse_content, reparse_markup_elements, TokenMode,
 };
 
 /// Allows partial refreshs of the [`Green`] node tree.
@@ -43,7 +42,7 @@ impl Reparser<'_> {
         mut offset: usize,
         outermost: bool,
     ) -> Option<Range<usize>> {
-        let child_mode = green.kind().mode().unwrap_or(TokenMode::Code);
+        let child_mode = green.kind().only_in_mode().unwrap_or(TokenMode::Code);
         let original_count = green.children().len();
 
         let mut search = SearchState::default();
@@ -137,7 +136,7 @@ impl Reparser<'_> {
             let superseded_span = pos.offset .. pos.offset + prev_len;
             let func: Option<ReparseMode> = match child.kind() {
                 NodeKind::CodeBlock => Some(ReparseMode::Code),
-                NodeKind::TemplateBlock => Some(ReparseMode::Template),
+                NodeKind::ContentBlock => Some(ReparseMode::Content),
                 _ => None,
             };
 
@@ -168,7 +167,7 @@ impl Reparser<'_> {
 
             if start.offset == self.replace_range.start
                 || ahead_kind.only_at_start()
-                || ahead_kind.mode() != Some(TokenMode::Markup)
+                || ahead_kind.only_in_mode() != Some(TokenMode::Markup)
             {
                 start = ahead;
                 at_start = ahead_at_start;
@@ -216,7 +215,7 @@ impl Reparser<'_> {
                 &self.src[newborn_span.start ..],
                 newborn_span.len(),
             ),
-            ReparseMode::Template => reparse_template(
+            ReparseMode::Content => reparse_content(
                 &prefix,
                 &self.src[newborn_span.start ..],
                 newborn_span.len(),
@@ -294,23 +293,13 @@ impl SearchState {
 enum ReparseMode {
     /// Reparse a code block, including its braces.
     Code,
-    /// Reparse a template block, including its square brackets.
-    Template,
+    /// Reparse a content block, including its square brackets.
+    Content,
     /// Reparse elements of the markup. The variant carries whether the node is
     /// `at_start` and the minimum indent of the containing markup node.
     MarkupElements(bool, usize),
 }
 
-impl NodeKind {
-    /// Whether this node has to appear at the start of a line.
-    pub fn only_at_start(&self) -> bool {
-        match self {
-            Self::Heading | Self::Enum | Self::List => true,
-            _ => false,
-        }
-    }
-}
-
 #[cfg(test)]
 #[rustfmt::skip]
 mod tests {
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 833a5b339..f07fefce3 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -52,10 +52,10 @@ pub fn reparse_block(
     Some((vec![first], terminated, 1))
 }
 
-/// Reparse a template literal.
+/// Reparse a content block.
 ///
 /// Returns `Some` if all of the input was consumed.
-pub fn reparse_template(
+pub fn reparse_content(
     prefix: &str,
     src: &str,
     end_pos: usize,
@@ -65,7 +65,7 @@ pub fn reparse_template(
         return None;
     }
 
-    template(&mut p);
+    content(&mut p);
 
     let (mut green, terminated) = p.consume()?;
     let first = green.remove(0);
@@ -152,7 +152,7 @@ pub fn reparse_markup_elements(
 /// Parse markup.
 ///
 /// If `at_start` is true, things like headings that may only appear at the
-/// beginning of a line or template are initially allowed.
+/// beginning of a line or content block are initially allowed.
 fn markup(p: &mut Parser, mut at_start: bool) {
     p.perform(NodeKind::Markup(0), |p| {
         while !p.eof() {
@@ -235,9 +235,9 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
         | NodeKind::Import
         | NodeKind::Include => markup_expr(p),
 
-        // Block and template.
+        // Code and content block.
         NodeKind::LeftBrace => block(p),
-        NodeKind::LeftBracket => template(p),
+        NodeKind::LeftBracket => content(p),
 
         NodeKind::Error(_, _) => p.eat(),
         _ => p.unexpected(),
@@ -424,7 +424,7 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult {
         // Structures.
         Some(NodeKind::LeftParen) => parenthesized(p, atomic),
         Some(NodeKind::LeftBrace) => Ok(block(p)),
-        Some(NodeKind::LeftBracket) => Ok(template(p)),
+        Some(NodeKind::LeftBracket) => Ok(content(p)),
 
         // Keywords.
         Some(NodeKind::Let) => let_expr(p),
@@ -679,9 +679,9 @@ fn block(p: &mut Parser) {
     });
 }
 
-// Parse a template block: `[...]`.
-fn template(p: &mut Parser) {
-    p.perform(NodeKind::TemplateBlock, |p| {
+// Parse a content block: `[...]`.
+fn content(p: &mut Parser) {
+    p.perform(NodeKind::ContentBlock, |p| {
         p.start_group(Group::Bracket);
         markup(p, true);
         p.end_group();
@@ -712,7 +712,7 @@ fn args(p: &mut Parser, direct: bool, brackets: bool) -> ParseResult {
         }
 
         while brackets && p.peek_direct() == Some(&NodeKind::LeftBracket) {
-            template(p);
+            content(p);
         }
     });
 
@@ -922,7 +922,7 @@ fn return_expr(p: &mut Parser) -> ParseResult {
 /// Parse a control flow body.
 fn body(p: &mut Parser) -> ParseResult {
     match p.peek() {
-        Some(NodeKind::LeftBracket) => Ok(template(p)),
+        Some(NodeKind::LeftBracket) => Ok(content(p)),
         Some(NodeKind::LeftBrace) => Ok(block(p)),
         _ => {
             p.expected("body");
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 425af0c1b..f48b445c7 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -221,8 +221,8 @@ pub enum Expr {
     Ident(Ident),
     /// A code block: `{ let x = 1; x + 2 }`.
     Code(CodeBlock),
-    /// A template block: `[*Hi* there!]`.
-    Template(TemplateBlock),
+    /// A content block: `[*Hi* there!]`.
+    Content(ContentBlock),
     /// A grouped expression: `(1 + 2)`.
     Group(GroupExpr),
     /// An array expression: `(1, "hi", 12cm)`.
@@ -270,7 +270,7 @@ impl TypedNode for Expr {
         match node.kind() {
             NodeKind::Ident(_) => node.cast().map(Self::Ident),
             NodeKind::CodeBlock => node.cast().map(Self::Code),
-            NodeKind::TemplateBlock => node.cast().map(Self::Template),
+            NodeKind::ContentBlock => node.cast().map(Self::Content),
             NodeKind::GroupExpr => node.cast().map(Self::Group),
             NodeKind::ArrayExpr => node.cast().map(Self::Array),
             NodeKind::DictExpr => node.cast().map(Self::Dict),
@@ -299,7 +299,7 @@ impl TypedNode for Expr {
         match self {
             Self::Lit(v) => v.as_red(),
             Self::Code(v) => v.as_red(),
-            Self::Template(v) => v.as_red(),
+            Self::Content(v) => v.as_red(),
             Self::Ident(v) => v.as_red(),
             Self::Array(v) => v.as_red(),
             Self::Dict(v) => v.as_red(),
@@ -419,14 +419,14 @@ impl CodeBlock {
 }
 
 node! {
-    /// A template block: `[*Hi* there!]`.
-    TemplateBlock: TemplateBlock
+    /// A content block: `[*Hi* there!]`.
+    ContentBlock: ContentBlock
 }
 
-impl TemplateBlock {
-    /// The contents of the template.
+impl ContentBlock {
+    /// The contained markup.
     pub fn body(&self) -> Markup {
-        self.0.cast_first_child().expect("template is missing body")
+        self.0.cast_first_child().expect("content is missing body")
     }
 }
 
diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs
index 20ea40396..c0e3376e4 100644
--- a/src/syntax/highlight.rs
+++ b/src/syntax/highlight.rs
@@ -203,7 +203,7 @@ impl Category {
             NodeKind::List => None,
             NodeKind::Enum => None,
             NodeKind::CodeBlock => None,
-            NodeKind::TemplateBlock => None,
+            NodeKind::ContentBlock => None,
             NodeKind::GroupExpr => None,
             NodeKind::ArrayExpr => None,
             NodeKind::DictExpr => None,
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index a3393eda9..e15cfabc2 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -579,9 +579,9 @@ pub enum NodeKind {
     From,
     /// The `as` keyword.
     As,
-    /// Template markup of which all lines must start in some column.
+    /// Markup of which all lines must start in some column.
     ///
-    /// Notably, the usize does not determine in which column the markup
+    /// Notably, the number does not determine in which column the markup
     /// started, but to the right of which column all markup elements must be,
     /// so it is zero except for headings and lists.
     Markup(usize),
@@ -644,8 +644,8 @@ pub enum NodeKind {
     Str(EcoString),
     /// A code block: `{ let x = 1; x + 2 }`.
     CodeBlock,
-    /// A template block: `[*Hi* there!]`.
-    TemplateBlock,
+    /// A content block: `[*Hi* there!]`.
+    ContentBlock,
     /// A grouped expression: `(1 + 2)`.
     GroupExpr,
     /// An array expression: `(1, "hi", 12cm)`.
@@ -763,8 +763,16 @@ impl NodeKind {
         }
     }
 
+    /// Whether this node has to appear at the start of a line.
+    pub fn only_at_start(&self) -> bool {
+        match self {
+            Self::Heading | Self::Enum | Self::List => true,
+            _ => false,
+        }
+    }
+
     /// Which mode this node can appear in, in both if `None`.
-    pub fn mode(&self) -> Option<TokenMode> {
+    pub fn only_in_mode(&self) -> Option<TokenMode> {
         match self {
             Self::Markup(_)
             | Self::Linebreak
@@ -782,7 +790,7 @@ impl NodeKind {
             | Self::List
             | Self::Raw(_)
             | Self::Math(_) => Some(TokenMode::Markup),
-            Self::TemplateBlock
+            Self::ContentBlock
             | Self::Space(_)
             | Self::Ident(_)
             | Self::CodeBlock
@@ -884,7 +892,7 @@ impl NodeKind {
             Self::Fraction(_) => "`fr` value",
             Self::Str(_) => "string",
             Self::CodeBlock => "code block",
-            Self::TemplateBlock => "template block",
+            Self::ContentBlock => "content block",
             Self::GroupExpr => "group",
             Self::ArrayExpr => "array",
             Self::DictExpr => "dictionary",
@@ -1008,7 +1016,7 @@ impl Hash for NodeKind {
             Self::Fraction(v) => v.to_bits().hash(state),
             Self::Str(v) => v.hash(state),
             Self::CodeBlock => {}
-            Self::TemplateBlock => {}
+            Self::ContentBlock => {}
             Self::GroupExpr => {}
             Self::ArrayExpr => {}
             Self::DictExpr => {}
diff --git a/tests/ref/structure/list.png b/tests/ref/structure/list.png
index 503bfc9a1..e8a38616e 100644
Binary files a/tests/ref/structure/list.png and b/tests/ref/structure/list.png differ
diff --git a/tests/typ/code/block.typ b/tests/typ/code/block.typ
index 9122ea486..194cde7a2 100644
--- a/tests/typ/code/block.typ
+++ b/tests/typ/code/block.typ
@@ -11,7 +11,7 @@
   for s in parts [{s}]
 }
 
-// Evaluates to join of the templates and strings.
+// Evaluates to join of the content and strings.
 {
   [How]
   if true {
@@ -52,13 +52,13 @@
 // Some things can't be joined.
 {
   [A]
-  // Error: 3-4 cannot join template with integer
+  // Error: 3-4 cannot join content with integer
   1
   [B]
 }
 
 ---
-// Block directly in template also creates a scope.
+// Block directly in markup also creates a scope.
 { let x = 1 }
 
 // Error: 7-8 unknown variable
@@ -103,7 +103,7 @@
 }
 
 ---
-// Template also creates a scope.
+// Content blocks also create a scope.
 [#let x = 1]
 
 // Error: 2-3 unknown variable
diff --git a/tests/typ/code/call.typ b/tests/typ/code/call.typ
index 5736c63b0..51ef1df59 100644
--- a/tests/typ/code/call.typ
+++ b/tests/typ/code/call.typ
@@ -63,7 +63,7 @@
 ---
 #let f(x) = x
 
-// Error: 1-6 expected callable or collection, found template
+// Error: 1-6 expected callable or collection, found content
 #f[1](2)
 
 ---
diff --git a/tests/typ/code/closure.typ b/tests/typ/code/closure.typ
index 14e74e7ef..6ef3cb289 100644
--- a/tests/typ/code/closure.typ
+++ b/tests/typ/code/closure.typ
@@ -2,7 +2,7 @@
 // Ref: false
 
 ---
-// Don't parse closure directly in template.
+// Don't parse closure directly in content.
 // Ref: true
 
 #let x = "\"hi\""
diff --git a/tests/typ/code/for.typ b/tests/typ/code/for.typ
index 661079fef..e161ba84f 100644
--- a/tests/typ/code/for.typ
+++ b/tests/typ/code/for.typ
@@ -28,7 +28,7 @@
    "]"
 }
 
-// Template body.
+// Content block body.
 // Should output `2345`.
 #for v in (1, 2, 3, 4, 5, 6, 7) [#if v >= 2 and v <= 5 { repr(v) }]
 
@@ -76,7 +76,7 @@
 
 // Return value.
 #test(for v in "" [], none)
-#test(type(for v in "1" []), "template")
+#test(type(for v in "1" []), "content")
 
 ---
 // Uniterable expression.
diff --git a/tests/typ/code/if.typ b/tests/typ/code/if.typ
index 3b3164fc6..0ab5c495f 100644
--- a/tests/typ/code/if.typ
+++ b/tests/typ/code/if.typ
@@ -16,7 +16,7 @@
   One.
 ]
 
-// Template in condition.
+// Content block in condition.
 #if [] != none [
   Two.
 ]
@@ -39,10 +39,10 @@
   "Four" + point
 }
 
-// Template can be argument or body depending on whitespace.
+// Content block can be argument or body depending on whitespace.
 {
-  if "template" == type[b] [Fi] else [Nope]
-  if "template" == type [Nope] else [ve.]
+  if "content" == type[b] [Fi] else [Nope]
+  if "content" == type [Nope] else [ve.]
 }
 
 #let i = 3
diff --git a/tests/typ/code/ops-invalid.typ b/tests/typ/code/ops-invalid.typ
index 340e4c9f9..184e20cfa 100644
--- a/tests/typ/code/ops-invalid.typ
+++ b/tests/typ/code/ops-invalid.typ
@@ -14,7 +14,7 @@
 #test({2*}, 2)
 
 ---
-// Error: 2-12 cannot apply '+' to template
+// Error: 2-12 cannot apply '+' to content
 {+([] + [])}
 
 ---
diff --git a/tests/typ/code/ops.typ b/tests/typ/code/ops.typ
index 53d00132e..b81fc8412 100644
--- a/tests/typ/code/ops.typ
+++ b/tests/typ/code/ops.typ
@@ -2,7 +2,7 @@
 // Ref: false
 
 ---
-// Test template addition.
+// Test adding content.
 // Ref: true
 {[*Hello* ] + [world!]}
 
@@ -130,7 +130,7 @@
 #test(test == test, true)
 #test((() => {}) == (() => {}), false)
 
-// Templates compare by some kind of equality.
+// Content compares by hash equality.
 #let t = [a]
 #test(t == t, true)
 #test([] == [], true)
diff --git a/tests/typ/code/repr.typ b/tests/typ/code/repr.typ
index 22890ff10..f766ee7ea 100644
--- a/tests/typ/code/repr.typ
+++ b/tests/typ/code/repr.typ
@@ -39,7 +39,7 @@
 {"a\n[]\"\u{1F680}string"}
 
 ---
-// Templates.
+// Content.
 {[*{"H" + "i"} there*]}
 
 ---
diff --git a/tests/typ/code/return.typ b/tests/typ/code/return.typ
index 9ee3aed75..d30164447 100644
--- a/tests/typ/code/return.typ
+++ b/tests/typ/code/return.typ
@@ -30,7 +30,7 @@
 #test(f(2), "ad")
 
 ---
-// Test return with joining and template.
+// Test return with joining and content.
 // Ref: true
 
 #let f(text, caption: none) = {
diff --git a/tests/typ/code/while.typ b/tests/typ/code/while.typ
index ccb679681..5dc5ae417 100644
--- a/tests/typ/code/while.typ
+++ b/tests/typ/code/while.typ
@@ -26,11 +26,11 @@
 #test(while false {}, none)
 
 #let i = 0
-#test(type(while i < 1 [{ i += 1 }]), "template")
+#test(type(while i < 1 [{ i += 1 }]), "content")
 
 ---
 // Condition must be boolean.
-// Error: 8-14 expected boolean, found template
+// Error: 8-14 expected boolean, found content
 #while [nope] [nope]
 
 ---
diff --git a/tests/typ/structure/enum.typ b/tests/typ/structure/enum.typ
index 8ba3cea6b..24c28147b 100644
--- a/tests/typ/structure/enum.typ
+++ b/tests/typ/structure/enum.typ
@@ -17,7 +17,7 @@
   . Indented
 
 ---
-// Test automatic numbering in summed templates.
+// Test automatic numbering in summed content.
 #for i in range(5) {
    [. #roman(1 + i)]
 }
@@ -55,6 +55,6 @@
 #set enum(label: "(())")
 
 ---
-// Error: 18-28 expected template, found boolean
+// Error: 18-28 expected content, found boolean
 #set enum(label: n => false)
 . A
diff --git a/tests/typ/structure/list.typ b/tests/typ/structure/list.typ
index 38fc2c630..67be16281 100644
--- a/tests/typ/structure/list.typ
+++ b/tests/typ/structure/list.typ
@@ -28,7 +28,7 @@ paragraphs.
 ---
 - Level 1
   - Level [
-2 through template
+2 through content block
 ]
 
 ---
diff --git a/tests/typ/style/set-site.typ b/tests/typ/style/set-site.typ
index dc9c9e02d..8ee8a5fd0 100644
--- a/tests/typ/style/set-site.typ
+++ b/tests/typ/style/set-site.typ
@@ -1,5 +1,5 @@
 // Test that set affects the instantiation site and not the
-// definition site of a template.
+// definition site of a content.
 
 ---
 // Test that text is affected by instantiation-site bold.
diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ
index 734ef931b..e442e4d57 100644
--- a/tests/typ/style/show.typ
+++ b/tests/typ/style/show.typ
@@ -39,7 +39,7 @@ A [= Heading] C
 = Heading
 
 ---
-// Error: 1-28 expected template, found string
+// Error: 1-28 expected content, found string
 #show heading(_, _) as "hi"
 = Heading
 
diff --git a/tests/typ/style/wrap.typ b/tests/typ/style/wrap.typ
index 9c94b9aea..b549f7d07 100644
--- a/tests/typ/style/wrap.typ
+++ b/tests/typ/style/wrap.typ
@@ -15,7 +15,7 @@ transports meaning from parchment to reader, the wave that sparks a flame
 in booklovers and the great fulfiller of human need.
 
 ---
-// Test wrap in template.
+// Test wrap in content block.
 A [_B #wrap c in [*#c*]; C_] D
 
 ---
diff --git a/tests/typ/text/emph-strong.typ b/tests/typ/text/emph-strong.typ
index 9bdb50590..cd2191ccf 100644
--- a/tests/typ/text/emph-strong.typ
+++ b/tests/typ/text/emph-strong.typ
@@ -7,7 +7,7 @@ _Emphasized and *strong* words!_
 // Inside of a word it's a normal underscore or star.
 hello_world Nutzer*innen
 
-// Can contain paragraph in child template.
+// Can contain paragraph in nested content block.
 _Still [
 
 ] emphasized._
diff --git a/tests/typ/utility/basics.typ b/tests/typ/utility/basics.typ
index 304fe7690..7fccc781b 100644
--- a/tests/typ/utility/basics.typ
+++ b/tests/typ/utility/basics.typ
@@ -36,7 +36,7 @@
 #test("(" + join("a", "b", "c", sep: ", ") + ")", "(a, b, c)")
 
 ---
-// Test joining templates.
+// Test content joining.
 // Ref: true
 #join([One], [Two], [Three], sep: [, ]).
 
@@ -72,7 +72,7 @@
 #float(float)
 
 ---
-// Error: 6-8 cannot convert template to string
+// Error: 6-8 cannot convert content to string
 #str([])
 
 ---
diff --git a/tests/typ/utility/collection.typ b/tests/typ/utility/collection.typ
index 649be4f79..e8be07b57 100644
--- a/tests/typ/utility/collection.typ
+++ b/tests/typ/utility/collection.typ
@@ -24,7 +24,7 @@
 #test(upper("Ελλάδα"), "ΕΛΛΆΔΑ")
 
 ---
-// Error: 8-9 expected string or template, found integer
+// Error: 8-9 expected string or content, found integer
 #upper(1)
 
 ---
@@ -35,9 +35,9 @@
 #test(sorted((2, 1, 3, 10, 5, 8, 6, -7, 2)), (-7, 1, 2, 2, 3, 5, 6, 8, 10))
 
 ---
-// Error: 9-21 cannot compare string with integer
+// Error: 9-21 cannot order string and integer
 #sorted((1, 2, "ab"))
 
 ---
-// Error: 9-24 cannot compare template with template
+// Error: 9-24 cannot order content and content
 #sorted(([Hi], [There]))
diff --git a/tests/typ/utility/math.typ b/tests/typ/utility/math.typ
index edbbd6826..ec62dbd23 100644
--- a/tests/typ/utility/math.typ
+++ b/tests/typ/utility/math.typ
@@ -54,7 +54,7 @@
 #min()
 
 ---
-// Error: 9-13 cannot compare integer with string
+// Error: 9-13 cannot compare integer and string
 #min(1, "hi")
 
 ---
diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json
index 577804fc2..ce683699a 100644
--- a/tools/support/typst.tmLanguage.json
+++ b/tools/support/typst.tmLanguage.json
@@ -14,17 +14,17 @@
           "patterns": [{ "include": "$self" }]
         },
         {
-          "name": "meta.block.typst",
+          "name": "meta.block.code.typst",
           "begin": "{",
           "end": "}",
-          "captures": { "0": { "name": "punctuation.definition.block.typst" } },
+          "captures": { "0": { "name": "punctuation.definition.block.code.typst" } },
           "patterns": [{ "include": "#code" }]
         },
         {
-          "name": "meta.template.typst",
+          "name": "meta.block.content.typst",
           "begin": "\\[",
           "end": "\\]",
-          "captures": { "0": { "name": "punctuation.definition.template.typst" } },
+          "captures": { "0": { "name": "punctuation.definition.block.content.typst" } },
           "patterns": [{ "include": "#markup" }]
         }
       ]