diff --git a/docs/external/graphviz.nml b/docs/external/graphviz.nml
new file mode 100644
index 0000000..06a5455
--- /dev/null
+++ b/docs/external/graphviz.nml
@@ -0,0 +1,32 @@
+@import ../template.nml
+%<make_doc({"External Tools"}, "Graphviz", "Graphvis")>%
+
+# Graphs from graphviz
+
+[graph]
+digraph {
+	bgcolor=transparent;
+
+	filelist [color=green, label="File List"];
+	doclist [color=green, label="Document List"];
+	iscached [shape=diamond, color=red, label="Cached?"];
+	parse [color=white, label=Parse];
+	compile [color=white, label=Compile];
+	cache [shape=box, color=blue, label=Cache];
+
+	edge [color=gray]
+	filelist -> iscached;
+	iscached -> cache[dir=both];
+	iscached -> doclist[label="+"];
+
+	iscached -> parse[label="No"];
+	parse -> compile;
+	compile -> cache[label="+"];
+	compile -> doclist[label="+"];
+
+	buildnav [color=white, label="Build Navigation"];
+	doclist -> buildnav;
+	output [color=white, label="Output"];
+	buildnav -> output;
+}
+[/graph]
diff --git a/docs/external/latex.nml b/docs/external/latex.nml
index 5f32a73..ee8e586 100644
--- a/docs/external/latex.nml
+++ b/docs/external/latex.nml
@@ -1,8 +1,5 @@
 @import ../template.nml
-@compiler.output = latex.html
-@nav.title = LaTeX
-@nav.category = External Tools
-@html.page_title = Documentation | LaTeX
+%<make_doc({"External Tools"}, "LaTeX", "LaTeX")>%
 
 @LaTeX = $|[kind=inline, caption=LaTeX]\LaTeX|$
 
diff --git a/docs/index.nml b/docs/index.nml
index 89dd307..1787221 100644
--- a/docs/index.nml
+++ b/docs/index.nml
@@ -1,6 +1,4 @@
 @import template.nml
-@compiler.output = index.html
-@nav.title = Documentation
-@html.page_title = Documentation | Index
+%<make_doc({}, "Index", "Index")>%
 
 # Welcome to the NML documentation!
diff --git a/docs/lua/lua.nml b/docs/lua/lua.nml
index 8766885..5c1f597 100644
--- a/docs/lua/lua.nml
+++ b/docs/lua/lua.nml
@@ -1,8 +1,5 @@
 @import ../template.nml
-@compiler.output = lua.html
-@nav.title = Lua
-@nav.category = Lua
-@html.page_title = Documentation | Lua
+%<make_doc({"Lua"}, "Lua", "Lua Basics")>%
 
 # Running lua code
 
diff --git a/docs/styles/basic.nml b/docs/styles/basic.nml
index 1284409..5c4a812 100644
--- a/docs/styles/basic.nml
+++ b/docs/styles/basic.nml
@@ -1,8 +1,5 @@
 @import ../template.nml
-@compiler.output = basic.html
-@nav.title = Basic
-@nav.category = Styles
-@html.page_title = Documentation | Basic Styles
+%<make_doc({"Styles"}, "Basic", "Basic Styles")>%
 
 # Basic styles
 ## Bold
diff --git a/docs/styles/user-defined.nml b/docs/styles/user-defined.nml
index 5980851..4082253 100644
--- a/docs/styles/user-defined.nml
+++ b/docs/styles/user-defined.nml
@@ -1,7 +1,4 @@
 @import ../template.nml
-@compiler.output = user-defined.html
-@nav.title = User-Defined
-@nav.category = Styles
-@html.page_title = Documentation | User-Defined Styles
+%<make_doc({"Styles"}, "User-Defined", "User-Defined Styles")>%
 
 # TODO
diff --git a/docs/template.nml b/docs/template.nml
index 8957313..3848b46 100644
--- a/docs/template.nml
+++ b/docs/template.nml
@@ -6,3 +6,22 @@
 \definecolor{__color1}{HTML}{d5d5d5} \\
 \everymath{\color{__color1}\displaystyle}
 @tex.main.block_prepend = \color{__color1}
+
+@<
+function make_doc(categories, title, page_title)
+	-- Navigation
+	nml.variable.insert("nav.title", title)
+	if categories[1] ~= nil
+	then
+		nml.variable.insert("nav.category", categories[1])
+		if categories[2] ~= nil
+		then
+			nml.variable.insert("nav.subcategory", categories[2])
+		end
+	end
+
+	-- HTML
+	nml.variable.insert("html.page_title", "NML | " .. page_title)
+	nml.variable.insert("compiler.output", page_title .. ".html")
+end
+>@
diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs
index 2b491ee..0d28d08 100644
--- a/src/compiler/compiler.rs
+++ b/src/compiler/compiler.rs
@@ -38,6 +38,24 @@ impl Compiler {
 		}
 	}
 
+	/// Sanitizes text for a [`Target`]
+	pub fn sanitize<S: AsRef<str>>(target: Target, str: S) -> String {
+		match target {
+			Target::HTML => str
+				.as_ref()
+				.replace("&", "&amp;")
+				.replace("<", "&lt;")
+				.replace(">", "&gt;")
+				.replace("\"", "&quot;"),
+			_ => todo!("Sanitize not implemented"),
+		}
+	}
+
+	/// Gets a reference name
+	pub fn refname<S: AsRef<str>>(target: Target, str: S) -> String {
+		Self::sanitize(target, str).replace(' ', "_")
+	}
+
 	/// Inserts or get a reference id for the compiled document
 	///
 	/// # Parameters
@@ -74,18 +92,6 @@ impl Compiler {
 		self.cache.as_ref().map(RefCell::borrow_mut)
 	}
 
-	pub fn sanitize<S: AsRef<str>>(target: Target, str: S) -> String {
-		match target {
-			Target::HTML => str
-				.as_ref()
-				.replace("&", "&amp;")
-				.replace("<", "&lt;")
-				.replace(">", "&gt;")
-				.replace("\"", "&quot;"),
-			_ => todo!("Sanitize not implemented"),
-		}
-	}
-
 	pub fn header(&self, document: &dyn Document) -> String {
 		pub fn get_variable_or_error(
 			document: &dyn Document,
@@ -109,8 +115,11 @@ impl Compiler {
 				result += "<!DOCTYPE HTML><html><head>";
 				result += "<meta charset=\"UTF-8\">";
 				if let Some(page_title) = get_variable_or_error(document, "html.page_title") {
-					result += format!("<title>{}</title>", Compiler::sanitize(self.target(), page_title.to_string()))
-						.as_str();
+					result += format!(
+						"<title>{}</title>",
+						Compiler::sanitize(self.target(), page_title.to_string())
+					)
+					.as_str();
 				}
 
 				if let Some(css) = document.get_variable("html.css") {
@@ -120,7 +129,7 @@ impl Compiler {
 					)
 					.as_str();
 				}
-				result += r#"</head><body><div id="layout">"#;
+				result += r#"</head><body><div class="layout">"#;
 
 				// TODO: TOC
 				// TODO: Author, Date, Title, Div
@@ -148,7 +157,7 @@ impl Compiler {
 		let header = self.header(document);
 
 		// Body
-		let mut body = r#"<div id="content">"#.to_string();
+		let mut body = r#"<div class="content">"#.to_string();
 		for i in 0..borrow.len() {
 			let elem = &borrow[i];
 
diff --git a/src/compiler/navigation.rs b/src/compiler/navigation.rs
index 395a73f..bed071d 100644
--- a/src/compiler/navigation.rs
+++ b/src/compiler/navigation.rs
@@ -23,7 +23,7 @@ impl NavEntry {
 		let mut result = String::new();
 		match target {
 			Target::HTML => {
-				result += r#"<div id="navbar"><ul>"#;
+				result += r#"<div class="navbar"><ul>"#;
 
 				fn process(
 					target: Target,
diff --git a/src/elements/section.rs b/src/elements/section.rs
index 1e85e5a..4625861 100644
--- a/src/elements/section.rs
+++ b/src/elements/section.rs
@@ -38,11 +38,14 @@ impl Element for Section {
 	fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
 	fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
 		match compiler.target() {
-			Target::HTML => Ok(format!(
-				"<h{0}>{1}</h{0}>",
+			Target::HTML => {
+				Ok(format!(
+				r#"<h{0} id="{1}">{2}</h{0}>"#,
 				self.depth,
+				Compiler::refname(compiler.target(), self.title.as_str()),
 				Compiler::sanitize(compiler.target(), self.title.as_str())
-			)),
+			))
+			},
 			Target::LATEX => Err("Unimplemented compiler".to_string()),
 		}
 	}
@@ -56,11 +59,27 @@ impl ReferenceableElement for Section {
 	fn compile_reference(
 		&self,
 		compiler: &Compiler,
-		document: &dyn Document,
+		_document: &dyn Document,
 		reference: &super::reference::Reference,
-		refid: usize,
+		_refid: usize,
 	) -> Result<String, String> {
-		todo!()
+		match compiler.target() {
+			Target::HTML => {
+				let caption = reference.caption().map_or(
+					format!(
+						"({})",
+						Compiler::sanitize(compiler.target(), self.title.as_str())
+					),
+					|cap| cap.clone(),
+				);
+
+				Ok(format!(
+					"<a class=\"section-ref\" href=\"#{}\">{caption}</a>",
+					Compiler::refname(compiler.target(), self.title.as_str())
+				))
+			}
+			_ => todo!(""),
+		}
 	}
 }
 
diff --git a/style.css b/style.css
index aa53616..f29798c 100644
--- a/style.css
+++ b/style.css
@@ -2,9 +2,19 @@ body {
 	background-color: #1b1b1d;
 	color: #c5c5c5;
 	font-family: sans-serif;
+	margin: 0;
+	padding: 0;
+}
 
-	max-width: 90ch;
+.layout {
+	display: flex;
+}
+
+.content {
+	max-width: 99ch;
 	margin: 0 auto;
+	padding: 0;
+	width: 100%;
 }
 
 /* Styles */
@@ -29,13 +39,18 @@ a.inline-code {
 }
 
 /* Navbar */
-#navbar {
+.navbar {
+	display: none;
+
     left: 0;
     top: 0;
     bottom: 0;
-    width: max(16vw, 20ch);
+    width: max(calc((100vw - 99ch) / 2 - 15vw), 24ch);
+	height: 100vh;
+	position: fixed;
+	margin-right: 1em;
+
 	overflow-y: auto;
-	position: absolute;
     box-sizing: border-box;
     overscroll-behavior-y: contain;
 
@@ -46,44 +61,53 @@ a.inline-code {
 	font-weight: bold;
 }
 
-#navbar a {
+@media (min-width: 130ch) {
+	.navbar {
+		display: block;
+	}
+	.container {
+		flex-direction: row;
+	}
+}
+
+.navbar a {
 	color: #ffb454;
 
 	text-decoration: none;
 	font-weight: normal;
 }
 
-#navbar li {
+.navbar li {
 	display: block;
 	position: relative;
 	padding-left: 1em;
 	margin-left: 0em;
 }
 
-#navbar ul {
+.navbar ul {
 	margin-left: 0em;
 	padding-left: 0;
 }
 
-#navbar summary{
+.navbar summary{
 	display: block;
 	cursor: pointer;
 }
 
-#navbar summary::marker,
-#navbar summary::-webkit-details-marker{
+.navbar summary::marker,
+.navbar summary::-webkit-details-marker{
 	display: none;
 }
 
-#navbar summary:focus{
+.navbar summary:focus{
 	outline: none;
 }
 
-#navbar summary:focus-visible{
+.navbar summary:focus-visible{
 	outline: 1px dotted #000;
 }
 
-#navbar summary:before {
+.navbar summary:before {
     content: "+";
     color: #ffb454;
     float: left;
@@ -91,7 +115,7 @@ a.inline-code {
     width: 1em;
 }
 
-#navbar details[open] > summary:before {
+.navbar details[open] > summary:before {
     content: "–";
 }