commit ef3aa81

shrub  ·  2026-05-17 21:29:15 +0000 UTC
parent ef3aa81
init
13 files changed,  +835, -0
A README
+3, -0
1@@ -0,0 +1,3 @@
2+out/
3+sites/shrub
4+sites/stress
+60, -0
 1@@ -0,0 +1,60 @@
 2+SHELL = /bin/sh
 3+
 4+SITE ?= example.com
 5+PORT ?= 8000
 6+SRC_DIR = sites/$(SITE)
 7+OUT_DIR = out/$(SITE)
 8+
 9+all: build
10+
11+include $(SRC_DIR)/manifest.mk
12+
13+SITE_TITLE ?= $(SITE)
14+SITE_SUBTITLE ?=
15+
16+RAW_HTML_TARGETS = $(HTML_TARGETS:.html=.raw.html)
17+BODY_HTML_TARGETS = $(HTML_TARGETS:.html=.body.html)
18+HELPER = ./bin/helper
19+
20+.PHONY: all build serve clean dirs
21+
22+build: dirs $(HTML_TARGETS) $(STATIC_TARGETS) $(OUT_DIR)/mandoc.css $(OUT_DIR)/pub/style.css
23+	@echo "mite: built $(SRC_DIR) -> $(OUT_DIR)"
24+
25+dirs:
26+	@mkdir -p $(DIRS)
27+
28+$(OUT_DIR)/%.raw.html: $(HELPER)
29+	@mkdir -p "$(dir $@)"
30+	$(HELPER) raw "$(SRC_DIR)" "$*" "$@"
31+
32+$(OUT_DIR)/%.body.html: $(OUT_DIR)/%.raw.html $(HELPER)
33+	@mkdir -p "$(dir $@)"
34+	$(HELPER) body "$<" "$@"
35+
36+$(OUT_DIR)/sidebar.html: $(SRC_DIR)/manifest.mk $(HELPER)
37+	@mkdir -p "$(dir $@)"
38+	$(HELPER) sidebar "$@" "$(SRC_DIR)"
39+
40+$(OUT_DIR)/%.html: $(OUT_DIR)/%.body.html $(OUT_DIR)/sidebar.html $(OUT_DIR)/mandoc.css $(OUT_DIR)/pub/style.css $(HELPER)
41+	@mkdir -p "$(dir $@)"
42+	$(HELPER) page "$@" "$*" "$(TITLE.$*)" "$(OS.$*)" "$(URL.$*)" "" "$(OUT_DIR)" "$(SITE_TITLE)" "$(SITE_SUBTITLE)" "$(URL.index)" "$(SRC_DIR)"
43+
44+$(STATIC_TARGETS):
45+	@mkdir -p "$(dir $@)"
46+	cp "$(SRC_DIR)/$(@:$(OUT_DIR)/%=%)" "$@"
47+
48+$(OUT_DIR)/pub/style.css: pub/style.css
49+	@mkdir -p "$(dir $@)"
50+	@cat "$<" > "$@"
51+	@if [ -f "$(SRC_DIR)/_mite/style.css" ]; then printf '\n' >> "$@"; cat "$(SRC_DIR)/_mite/style.css" >> "$@"; fi
52+
53+$(OUT_DIR)/mandoc.css: pub/mandoc.css
54+	@mkdir -p "$(dir $@)"
55+	@if [ -f "$(SRC_DIR)/mandoc.css" ]; then cp "$(SRC_DIR)/mandoc.css" "$@"; else cp pub/mandoc.css "$@"; fi
56+
57+serve: build
58+	$(HELPER) serve "$(OUT_DIR)" "$(PORT)"
59+
60+clean:
61+	rm -rf "$(OUT_DIR)"
A README
+12, -0
 1@@ -0,0 +1,12 @@
 2+mite
 3+----
 4+
 5+mite is a small static site generator which uses mandoc and make. right now you will need gnu make to build it, although very few gnu extensions are used.
 6+
 7+to build and serve the example site locally with shinobi and ninja:
 8+```
 9+make
10+make serve
11+```
12+
13+documentation and usage guide to come; you can take a look at the example site to get an idea.
+209, -0
  1@@ -0,0 +1,209 @@
  2+#!/bin/sh
  3+set -eu
  4+#helper script for mite.
  5+cmd="${1:-}"
  6+shift || true
  7+
  8+case "$cmd" in
  9+raw)
 10+	src_dir="$1"
 11+	rel="$2"
 12+	out="$3"
 13+	src=""
 14+	for sec in 1 2 3 3p 4 5 6 7 8 9 mdoc; do
 15+		cand="$src_dir/$rel.$sec"
 16+		if [ -f "$cand" ]; then
 17+			src="$cand"
 18+			break
 19+		fi
 20+	done
 21+	if [ -z "$src" ]; then
 22+		echo "helper raw: no source for $rel under $src_dir" >&2
 23+		exit 1
 24+	fi
 25+	mandoc -T html -O style=mandoc.css "$src" > "$out"
 26+	;;
 27+body)
 28+	in="$1"
 29+	out="$2"
 30+	awk 'BEGIN{inb=0;skip=0} \
 31+		/<[Bb][Oo][Dd][Yy][^>]*>/{inb=1; sub(/^.*<[Bb][Oo][Dd][Yy][^>]*>/,""); if(length($0))print; next} \
 32+		/<\/[Bb][Oo][Dd][Yy]>/ {sub(/<\/[Bb][Oo][Dd][Yy]>.*/,""); if(inb&&length($0))print; inb=0; next} \
 33+		{ if(!inb)next; if($0 ~ /<table class="head">/){skip=1;next} if($0 ~ /<table class="foot">/){skip=1;next} if(skip && $0 ~ /<\/table>/){skip=0;next} if(!skip)print }' "$in" \
 34+	| perl -0777 -pe 's{<div class="Bd[^"]*\bLi\b[^"]*">\s*<pre>(.*?)</pre>\s*</div>}{my $r=$1;my @l=grep{$_!~/^\s*$/}split/\n/,$r;my $ok=@l&&!grep{$_!~/^\s*&lt;.*&gt;\s*$/}@l;if($ok){$r=~s/&lt;/</g;$r=~s/&gt;/>/g;$r=~s/&quot;/"/g;$r}else{qq{<div class="Bd Li"><pre>$1</pre></div>}}}gse' \
 35+	> "$out"
 36+	;;
 37+sidebar)
 38+	out="$1"
 39+	src_dir="$2"
 40+	{
 41+		echo '<ul class="side-nav side-tree">'
 42+		awk '
 43+			function flush() {
 44+				if (page != "" && nav != "" && url != "") {
 45+					if (page == "index") return
 46+					u = url
 47+					gsub(/\$\(PAGE\)/, page, u)
 48+					printf "%s|%s|%s\n", page, u, nav
 49+				}
 50+			}
 51+			FNR==1 {
 52+				if (NR > 1) flush()
 53+				page=""; nav=""; url=""
 54+			}
 55+			/^PAGE := / { page=substr($0, 9) }
 56+			/^NAV\.\$\(PAGE\) = / { nav=substr($0, 15) }
 57+			/^URL\.\$\(PAGE\) := / { url=substr($0, 16) }
 58+			END { flush() }
 59+		' "$src_dir"/mk/*.mk \
 60+		| sort \
 61+		| awk -F'|' '
 62+			function close_to(n,    i) {
 63+				for (i=level; i>n; i--) {
 64+					print "</ul></details></li>"
 65+				}
 66+				level = n
 67+			}
 68+			BEGIN { level=0 }
 69+			{
 70+				path=$1; url=$2; nav=$3
 71+				n = split(path, p, "/")
 72+				dirs = n - 1
 73+				common = 0
 74+				for (i=1; i<=dirs && i<=level; i++) {
 75+					if (p[i] == dstack[i]) common = i
 76+					else break
 77+				}
 78+				close_to(common)
 79+				for (i=common+1; i<=dirs; i++) {
 80+					dstack[i] = p[i]
 81+					dirpath = p[1]
 82+					for (j=2; j<=i; j++) dirpath = dirpath "/" p[j]
 83+					printf "<li class=\"dir\"><details data-dir=\"%s\"><summary>%s</summary><ul>\n", dirpath, p[i]
 84+				}
 85+				level = dirs
 86+				printf "<li class=\"page\"><a href=\"%s\">&rsaquo; %s</a></li>\n", url, nav
 87+			}
 88+			END { close_to(0) }
 89+		'
 90+		echo '</ul>'
 91+	} > "$out"
 92+	;;
 93+page)
 94+	out="$1"
 95+	rel="$2"
 96+	title="$3"
 97+	os="$4"
 98+	url="$5"
 99+	label="$6"
100+	out_dir="$7"
101+	site_title="$8"
102+	site_subtitle="${9:-}"
103+	home_url="${10:-}"
104+	src_dir="${11:-}"
105+	[ -n "$home_url" ] || home_url="/index.html"
106+	raw="$out_dir/$rel.raw.html"
107+	site_id="$(basename "$out_dir")"
108+	style_href="/pub/style.css?site=$site_id"
109+	mandoc_href="/mandoc.css?site=$site_id"
110+	# get label from source filename extension, example.1 -> example(1)
111+	label="$title"
112+	if [ -n "$src_dir" ]; then
113+		src=""
114+		for sec in 1 2 3 3p 4 5 6 7 8 9 mdoc; do
115+			cand="$src_dir/$rel.$sec"
116+			if [ -f "$cand" ]; then
117+				src="$cand"
118+				break
119+			fi
120+		done
121+		if [ -n "$src" ]; then
122+			base="$(basename "$src")"
123+			name="${base%.*}"
124+			sec="${base##*.}"
125+			label="${name}(${sec})"
126+		fi
127+	fi
128+	date_dd="$(awk 'match($0, /<td class="foot-date">[^<]*<\/td>/){v=substr($0, RSTART, RLENGTH); gsub(/<[^>]*>/, "", v); print v; exit}' "$raw")"
129+	if [ -z "$date_dd" ]; then
130+		# do current date if none
131+		date_dd="$(LC_ALL=C date -u '+%B %e, %Y' | awk '{$1=$1; print}')"
132+	fi
133+
134+	{
135+		if [ -f "$src_dir/_mite/header.html" ]; then
136+			awk -v t="$title" -v s="$style_href" -v m="$mandoc_href" '{
137+				gsub(/@TITLE@/, t)
138+				gsub(/href="\/pub\/style\.css"/, "href=\"" s "\"")
139+				gsub(/href="\/mandoc\.css"/, "href=\"" m "\"")
140+				print
141+			}' "$src_dir/_mite/header.html"
142+		else
143+			printf '<!doctype html>\n<html>\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1">\n<title>%s</title>\n<link rel="stylesheet" href="%s">\n<link rel="stylesheet" href="%s">\n</head>\n<body>\n' "$title" "$mandoc_href" "$style_href"
144+		fi
145+
146+		echo '<div class="layout">'
147+		echo '<aside class="sidebar">'
148+		printf '<div class="sidebar-title"><a href="%s">%s</a></div>\n' "$home_url" "$site_title"
149+		if [ -n "$site_subtitle" ]; then
150+			printf '<div class="sidebar-subtitle">%s</div>\n' "$site_subtitle"
151+		fi
152+		awk -v cur="$url" '{ line=$0; if (index(line, "href=\"" cur "\"")>0) sub(/<a /,"<a class=\"thisPage\" ",line); print line }' "$out_dir/sidebar.html"
153+		echo '<div class="sidebar-powered">powered by make n mdoc</div>'
154+		cat <<'EOF'
155+<script>
156+(function () {
157+  var key = "mite.sidebar.open";
158+  var state = {};
159+  try { state = JSON.parse(localStorage.getItem(key) || "{}"); } catch (_) {}
160+  var details = document.querySelectorAll(".sidebar details[data-dir]");
161+  for (var i = 0; i < details.length; i++) {
162+    var d = details[i];
163+    var id = d.getAttribute("data-dir");
164+    if (Object.prototype.hasOwnProperty.call(state, id)) d.open = !!state[id];
165+  }
166+  var cur = document.querySelector(".sidebar a.thisPage");
167+  if (cur) {
168+    var p = cur.parentElement;
169+    while (p && p !== document.body) {
170+      if (p.tagName === "DETAILS") p.open = true;
171+      p = p.parentElement;
172+    }
173+  }
174+  for (var j = 0; j < details.length; j++) {
175+    details[j].addEventListener("toggle", function () {
176+      state[this.getAttribute("data-dir")] = this.open;
177+      try { localStorage.setItem(key, JSON.stringify(state)); } catch (_) {}
178+    });
179+  }
180+})();
181+</script>
182+EOF
183+		echo '</aside>'
184+		echo '<main class="content">'
185+		printf '<div class="head" role="doc-pageheader" aria-label="Manual header line"><span class="head-ltitle">%s</span> <span class="head-vol">%s</span> <span class="head-rtitle">%s</span></div>\n' "$label" "$title" "$label"
186+		cat "$out_dir/$rel.body.html"
187+		printf '<div class="foot" role="doc-pagefooter" aria-label="Manual footer line"><span class="foot-left"></span><span class="foot-date">%s</span> <span class="foot-os foot-right">%s</span></div>\n' "$date_dd" "$os"
188+		echo '</main>'
189+		echo '</div>'
190+
191+		if [ -f "$src_dir/_mite/footer.html" ]; then
192+			cat "$src_dir/_mite/footer.html"
193+		else
194+			printf '</body>\n</html>\n'
195+		fi
196+	} > "$out"
197+	;;
198+serve)
199+	out_dir="$1"
200+	port="${2:-8000}"
201+	cd "$out_dir"
202+	echo "serve: http://127.0.0.1:$port/"
203+	#you need python, duh. this is really just for quick local preview
204+	python3 -m http.server "$port"
205+	;;
206+*)
207+	echo "usage: $0 {raw|body|sidebar|page|serve} ..." >&2
208+	exit 1
209+	;;
210+esac
+369, -0
  1@@ -0,0 +1,369 @@
  2+/* $OpenBSD: mandoc.css,v 1.42 2025/06/26 16:59:35 schwarze Exp $ */
  3+/*
  4+ * Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
  5+ *
  6+ * Written by Ingo Schwarze <schwarze@openbsd.org>.
  7+ * I place this file into the public domain.
  8+ * Permission to use, copy, modify, and distribute it for any purpose
  9+ * with or without fee is hereby granted, without any conditions.
 10+ */
 11+
 12+/* Global defaults. */
 13+
 14+html {		max-width: 65em;
 15+		--bg: #FFFFFF;
 16+		--fg: #000000; }
 17+body {		background: var(--bg);
 18+		color: var(--fg);
 19+		font-family: Helvetica,Arial,sans-serif; }
 20+h1, h2 {	font-size: 110%; }
 21+table {		margin-top: 0em;
 22+		margin-bottom: 0em;
 23+		border-collapse: collapse; }
 24+/* Some browsers set border-color in a browser style for tbody,
 25+ * but not for table, resulting in inconsistent border styling. */
 26+tbody {		border-color: inherit; }
 27+tr {		border-color: inherit; }
 28+td {		vertical-align: top;
 29+		padding-left: 0.2em;
 30+		padding-right: 0.2em;
 31+		border-color: inherit; }
 32+ul, ol, dl {	margin-top: 0em;
 33+		margin-bottom: 0em; }
 34+li, dt {	margin-top: 1em; }
 35+pre {		font-family: inherit; }
 36+
 37+.permalink {	border-bottom: thin dotted;
 38+		color: inherit;
 39+		font: inherit;
 40+		text-decoration: inherit; }
 41+* {		clear: both }
 42+
 43+/* Search form and search results. */
 44+
 45+fieldset {	border: thin solid silver;
 46+		border-radius: 1em;
 47+		text-align: center; }
 48+input[name=expr] {
 49+		width: 25%; }
 50+
 51+table.results {	margin-top: 1em;
 52+		margin-left: 2em;
 53+		font-size: smaller; }
 54+
 55+/* Header and footer lines. */
 56+
 57+div[role=doc-pageheader] {
 58+		display: flex;
 59+		border-bottom: 1px dotted #808080;
 60+		margin-bottom: 1em;
 61+		font-size: smaller; }
 62+.head-ltitle {	flex: 1; }
 63+.head-vol {	flex: 0 1 auto;
 64+		text-align: center; }
 65+.head-rtitle {	flex: 1;
 66+		text-align: right; }
 67+
 68+div[role=doc-pagefooter] {
 69+		display: flex;
 70+		justify-content: space-between;
 71+		border-top: 1px dotted #808080;
 72+		margin-top: 1em;
 73+		font-size: smaller; }
 74+.foot-left {	flex: 1; }
 75+.foot-date {	flex: 0 1 auto;
 76+		text-align: center; }
 77+.foot-right {	flex: 1;
 78+		text-align: right; }
 79+
 80+/* Sections and paragraphs. */
 81+
 82+main {		margin-left: 3.8em; }
 83+.Nd { }
 84+section.Sh { }
 85+h2.Sh {		margin-top: 1.2em;
 86+		margin-bottom: 0.6em;
 87+		margin-left: -3.2em; }
 88+section.Ss { }
 89+h3.Ss {		margin-top: 1.2em;
 90+		margin-bottom: 0.6em;
 91+		margin-left: -1.2em;
 92+		font-size: 105%; }
 93+.Pp {		margin: 0.6em 0em; }
 94+.Sx { }
 95+.Xr { }
 96+
 97+/* Displays and lists. */
 98+
 99+.Bd { }
100+.Bd-indent {	margin-left: 3.8em; }
101+
102+.Bl-bullet {	list-style-type: disc;
103+		padding-left: 1em; }
104+.Bl-bullet > li { }
105+.Bl-dash {	list-style-type: none;
106+		padding-left: 0em; }
107+.Bl-dash > li:before {
108+		content: "\2014  "; }
109+.Bl-item {	list-style-type: none;
110+		padding-left: 0em; }
111+.Bl-item > li { }
112+.Bl-compact > li {
113+		margin-top: 0em; }
114+
115+.Bl-enum {	padding-left: 2em; }
116+.Bl-enum > li { }
117+.Bl-compact > li {
118+		margin-top: 0em; }
119+
120+.Bl-diag { }
121+.Bl-diag > dt {
122+		font-style: normal;
123+		font-weight: bold; }
124+.Bl-diag > dd {
125+		margin-left: 0em; }
126+.Bl-hang { }
127+.Bl-hang > dt { }
128+.Bl-hang > dd {
129+		margin-left: 5.5em; }
130+.Bl-inset { }
131+.Bl-inset > dt { }
132+.Bl-inset > dd {
133+		margin-left: 0em; }
134+.Bl-ohang { }
135+.Bl-ohang > dt { }
136+.Bl-ohang > dd {
137+		margin-left: 0em; }
138+.Bl-tag {	margin-top: 0.6em;
139+		margin-left: 5.5em; }
140+.Bl-tag > dt {
141+		float: left;
142+		margin-top: 0em;
143+		margin-left: -5.5em;
144+		padding-right: 0.5em;
145+		vertical-align: top; }
146+.Bl-tag > dd {
147+		clear: right;
148+		column-count: 1;  /* Force block formatting context. */
149+		width: 100%;
150+		margin-top: 0em;
151+		margin-left: 0em;
152+		margin-bottom: 0.6em;
153+		vertical-align: top; }
154+.Bl-compact {	margin-top: 0em; }
155+.Bl-compact > dd {
156+		margin-bottom: 0em; }
157+.Bl-compact > dt {
158+		margin-top: 0em; }
159+
160+.Bl-column { }
161+.Bl-column > tbody > tr { }
162+.Bl-column > tbody > tr > td {
163+		margin-top: 1em; }
164+.Bl-compact > tbody > tr > td {
165+		margin-top: 0em; }
166+
167+.Rs {		font-style: normal;
168+		font-weight: normal; }
169+.RsA { }
170+.RsB {		font-style: italic;
171+		font-weight: normal; }
172+.RsC { }
173+.RsD { }
174+.RsI {		font-style: italic;
175+		font-weight: normal; }
176+.RsJ {		font-style: italic;
177+		font-weight: normal; }
178+.RsN { }
179+.RsO { }
180+.RsP { }
181+.RsQ { }
182+.RsR { }
183+.RsT {		font-style: normal;
184+		font-weight: normal; }
185+.RsU { }
186+.RsV { }
187+
188+.eqn { }
189+.tbl td {	vertical-align: middle; }
190+
191+.HP {		margin-left: 3.8em;
192+		text-indent: -3.8em; }
193+
194+/* Semantic markup for command line utilities. */
195+
196+table.Nm { }
197+code.Nm {	font-style: normal;
198+		font-weight: bold;
199+		font-family: inherit; }
200+.Fl {		font-style: normal;
201+		font-weight: bold;
202+		font-family: inherit; }
203+.Cm {		font-style: normal;
204+		font-weight: bold;
205+		font-family: inherit; }
206+.Ar {		font-style: italic;
207+		font-weight: normal; }
208+.Op {		display: inline flow; }
209+.Ic {		font-style: normal;
210+		font-weight: bold;
211+		font-family: inherit; }
212+.Ev {		font-style: normal;
213+		font-weight: normal;
214+		font-family: monospace; }
215+.Pa {		font-style: italic;
216+		font-weight: normal; }
217+
218+/* Semantic markup for function libraries. */
219+
220+.Lb { }
221+code.In {	font-style: normal;
222+		font-weight: bold;
223+		font-family: inherit; }
224+a.In { }
225+.Fd {		font-style: normal;
226+		font-weight: bold;
227+		font-family: inherit; }
228+.Ft {		font-style: italic;
229+		font-weight: normal; }
230+.Fn {		font-style: normal;
231+		font-weight: bold;
232+		font-family: inherit; }
233+.Fa {		font-style: italic;
234+		font-weight: normal; }
235+.Vt {		font-style: italic;
236+		font-weight: normal; }
237+.Va {		font-style: italic;
238+		font-weight: normal; }
239+.Dv {		font-style: normal;
240+		font-weight: normal;
241+		font-family: monospace; }
242+.Er {		font-style: normal;
243+		font-weight: normal;
244+		font-family: monospace; }
245+
246+/* Various semantic markup. */
247+
248+.An { }
249+.Lk { }
250+.Mt { }
251+.Cd {		font-style: normal;
252+		font-weight: bold;
253+		font-family: inherit; }
254+.Ad {		font-style: italic;
255+		font-weight: normal; }
256+.Ms {		font-style: normal;
257+		font-weight: bold; }
258+.St { }
259+.Ux { }
260+
261+/* Physical markup. */
262+
263+.Bf {		display: inline flow; }
264+.No {		font-style: normal;
265+		font-weight: normal; }
266+.Em {		font-style: italic;
267+		font-weight: normal; }
268+.Sy {		font-style: normal;
269+		font-weight: bold; }
270+.Li {		font-style: normal;
271+		font-weight: normal;
272+		font-family: monospace; }
273+
274+/* Tooltip support. */
275+
276+h2.Sh, h3.Ss {	position: relative; }
277+.An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
278+.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
279+.St, .Sx, .Sy, .Va, .Vt, .Xr {
280+		display: inline flow;
281+		position: relative; }
282+
283+.An::before {	content: "An"; }
284+.Ar::before {	content: "Ar"; }
285+.Cd::before {	content: "Cd"; }
286+.Cm::before {	content: "Cm"; }
287+.Dv::before {	content: "Dv"; }
288+.Em::before {	content: "Em"; }
289+.Er::before {	content: "Er"; }
290+.Ev::before {	content: "Ev"; }
291+.Fa::before {	content: "Fa"; }
292+.Fd::before {	content: "Fd"; }
293+.Fl::before {	content: "Fl"; }
294+.Fn::before {	content: "Fn"; }
295+.Ft::before {	content: "Ft"; }
296+.Ic::before {	content: "Ic"; }
297+code.In::before { content: "In"; }
298+.Lb::before {	content: "Lb"; }
299+.Lk::before {	content: "Lk"; }
300+.Ms::before {	content: "Ms"; }
301+.Mt::before {	content: "Mt"; }
302+.Nd::before {	content: "Nd"; }
303+code.Nm::before { content: "Nm"; }
304+.Pa::before {	content: "Pa"; }
305+.Rs::before {	content: "Rs"; }
306+h2.Sh::before {	content: "Sh"; }
307+h3.Ss::before {	content: "Ss"; }
308+.St::before {	content: "St"; }
309+.Sx::before {	content: "Sx"; }
310+.Sy::before {	content: "Sy"; }
311+.Va::before {	content: "Va"; }
312+.Vt::before {	content: "Vt"; }
313+.Xr::before {	content: "Xr"; }
314+
315+.An::before, .Ar::before, .Cd::before, .Cm::before,
316+.Dv::before, .Em::before, .Er::before, .Ev::before,
317+.Fa::before, .Fd::before, .Fl::before, .Fn::before, .Ft::before,
318+.Ic::before, code.In::before, .Lb::before, .Lk::before,
319+.Ms::before, .Mt::before, .Nd::before, code.Nm::before,
320+.Pa::before, .Rs::before,
321+h2.Sh::before, h3.Ss::before, .St::before, .Sx::before, .Sy::before,
322+.Va::before, .Vt::before, .Xr::before {
323+		opacity: 0;
324+		transition: .15s ease opacity;
325+		pointer-events: none;
326+		position: absolute;
327+		bottom: 100%;
328+		box-shadow: 0 0 .35em var(--fg);
329+		padding: .15em .25em;
330+		white-space: nowrap;
331+		font-family: Helvetica,Arial,sans-serif;
332+		font-style: normal;
333+		font-weight: bold;
334+		background: var(--bg);
335+		color: var(--fg); }
336+.An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before,
337+.Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before,
338+.Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before,
339+.Ft:hover::before, .Ic:hover::before, code.In:hover::before,
340+.Lb:hover::before, .Lk:hover::before, .Ms:hover::before, .Mt:hover::before,
341+.Nd:hover::before, code.Nm:hover::before, .Pa:hover::before,
342+.Rs:hover::before, h2.Sh:hover::before, h3.Ss:hover::before, .St:hover::before,
343+.Sx:hover::before, .Sy:hover::before, .Va:hover::before, .Vt:hover::before,
344+.Xr:hover::before {
345+		opacity: 1;
346+		pointer-events: inherit; }
347+
348+/* Overrides to avoid excessive margins on small devices. */
349+
350+@media (max-width: 37.5em) {
351+main {		margin-left: 0.5em; }
352+h2.Sh, h3.Ss {	margin-left: 0em; }
353+.Bd-indent {	margin-left: 2em; }
354+.Bl-hang > dd {
355+		margin-left: 2em; }
356+.Bl-tag {	margin-left: 2em; }
357+.Bl-tag > dt {
358+		margin-left: -2em; }
359+.HP {		margin-left: 2em;
360+		text-indent: -2em; }
361+}
362+
363+/* Overrides for a dark color scheme for accessibility. */
364+
365+@media (prefers-color-scheme: dark) {
366+html {		--bg: #1E1F21;
367+		--fg: #EEEFF1; }
368+:link {		color: #BAD7FF; }
369+:visited {	color: #F6BAFF; }
370+}
+104, -0
  1@@ -0,0 +1,104 @@
  2+body {
  3+    display: flex;
  4+    min-height: 100vh;
  5+}
  6+
  7+.layout {
  8+    display: flex;
  9+    width: 100%;
 10+}
 11+
 12+.sidebar {
 13+    flex: 0 0 16%;
 14+    padding: 20px;
 15+    align-self: flex-start;
 16+}
 17+
 18+.sidebar-title {
 19+    display: block;
 20+    border-bottom: 1px dotted #808080;
 21+    margin-bottom: 0.25em;
 22+    font-size: 110%;
 23+    font-weight: 700;
 24+}
 25+.sidebar-subtitle {
 26+    color: #676767;
 27+    font-size: 90%;
 28+    margin-bottom: 1em;
 29+}
 30+
 31+.content {
 32+    flex: 0 0 74%;
 33+    padding: 20px;
 34+    margin-bottom: 20px;
 35+    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
 36+    background: var(--bg);
 37+}
 38+
 39+.side-nav {
 40+    list-style: none;
 41+    margin: 0;
 42+    padding: 0;
 43+}
 44+.side-tree,
 45+.side-tree ul {
 46+    list-style: none;
 47+    margin: 0;
 48+    padding-left: 0;
 49+}
 50+.side-tree ul {
 51+    margin-left: 0.8em;
 52+}
 53+.side-tree summary {
 54+    cursor: pointer;
 55+    user-select: none;
 56+}
 57+.side-tree summary::marker {
 58+    color: #676767;
 59+}
 60+.side-tree .dir {
 61+    margin-top: 0.3em;
 62+}
 63+
 64+.side-nav li {
 65+    margin-top: 0.3em;
 66+}
 67+
 68+.side-nav .d1 { margin-left: 0.5em; }
 69+.side-nav .d2 { margin-left: 1em; }
 70+.side-nav .d3 { margin-left: 1.5em; }
 71+.side-nav .d4 { margin-left: 2em; }
 72+
 73+.side-nav a {
 74+    text-decoration: none;
 75+}
 76+
 77+.side-nav a.thisPage {
 78+    font-weight: 700;
 79+    text-decoration: underline;
 80+}
 81+
 82+.sidebar-powered {
 83+    margin-top: 1.25em;
 84+    padding-top: 0.75em;
 85+    border-top: 1px dotted #808080;
 86+    color: #676767;
 87+    font-size: 90%;
 88+}
 89+
 90+@media (max-width: 900px) {
 91+    body, .layout {
 92+        flex-direction: column;
 93+    }
 94+
 95+    .sidebar,
 96+    .content {
 97+        flex: 0 0 auto;
 98+        width: 100%;
 99+    }
100+
101+    .content {
102+        box-shadow: none;
103+        margin-bottom: 0;
104+    }
105+}
+5, -0
1@@ -0,0 +1,5 @@
2+.Dd May 17, 2026
3+.Dt ABOUT 7
4+.Os "example.com"
5+.Sh "about"
6+this is the second example page in a subdirectory
+5, -0
1@@ -0,0 +1,5 @@
2+.Dd May 17, 2026
3+.Dt GETTING-STARTED 7
4+.Os "example.com"
5+.Sh "getting started"
6+this is the first example page in a subdirectory.
+8, -0
1@@ -0,0 +1,8 @@
2+.Dd May 17, 2026
3+.Dt HOME 7
4+.Os "example.com"
5+.Sh "welcome"
6+this is the example site for mite!
7+
8+makefiles and mandoc makes the world go round.
9+.Pp
+14, -0
 1@@ -0,0 +1,14 @@
 2+SITE_TITLE = example.com
 3+SITE_SUBTITLE = werc users go home!
 4+
 5+DIRS = \
 6+	$(OUT_DIR) \
 7+	$(OUT_DIR)/docs \
 8+	$(OUT_DIR)/pub
 9+
10+MDOC_FILES =
11+HTML_TARGETS =
12+STATIC_TARGETS =
13+META_PAGES =
14+
15+include $(SRC_DIR)/mk/*
+16, -0
 1@@ -0,0 +1,16 @@
 2+PAGE := docs/about
 3+MDOC_FILES := $(MDOC_FILES) $(PAGE).1
 4+HTML_TARGETS := $(HTML_TARGETS) $(OUT_DIR)/$(PAGE).html
 5+META_PAGES := $(META_PAGES) $(PAGE)
 6+
 7+TITLE.$(PAGE) = about
 8+OS.$(PAGE) = example.com
 9+URL.$(PAGE) := /$(PAGE).html
10+LABEL.$(PAGE) = about(7)
11+NAV.$(PAGE) = about
12+DEPTH.$(PAGE) = 1
13+
14+$(OUT_DIR)/docs/about.html: $(SRC_DIR)/mk/docs-about.mk
15+
16+
17+$(OUT_DIR)/docs/about.raw.html: $(SRC_DIR)/docs/about.1
+16, -0
 1@@ -0,0 +1,16 @@
 2+PAGE := docs/getting-started
 3+MDOC_FILES := $(MDOC_FILES) $(PAGE).1
 4+HTML_TARGETS := $(HTML_TARGETS) $(OUT_DIR)/$(PAGE).html
 5+META_PAGES := $(META_PAGES) $(PAGE)
 6+
 7+TITLE.$(PAGE) = getting started
 8+OS.$(PAGE) = example.com
 9+URL.$(PAGE) := /$(PAGE).html
10+LABEL.$(PAGE) = getting-started(7)
11+NAV.$(PAGE) = getting started
12+DEPTH.$(PAGE) = 1
13+
14+$(OUT_DIR)/docs/getting-started.html: $(SRC_DIR)/mk/docs-getting-started.mk
15+
16+
17+$(OUT_DIR)/docs/getting-started.raw.html: $(SRC_DIR)/docs/getting-started.1
+14, -0
 1@@ -0,0 +1,14 @@
 2+PAGE := index
 3+MDOC_FILES := $(MDOC_FILES) $(PAGE).1
 4+HTML_TARGETS := $(HTML_TARGETS) $(OUT_DIR)/$(PAGE).html
 5+META_PAGES := $(META_PAGES) $(PAGE)
 6+
 7+TITLE.$(PAGE) = home
 8+OS.$(PAGE) = example.com
 9+URL.$(PAGE) := /$(PAGE).html
10+LABEL.$(PAGE) = home(7)
11+NAV.$(PAGE) = home
12+DEPTH.$(PAGE) = 0
13+
14+$(OUT_DIR)/index.html: $(SRC_DIR)/mk/index.mk
15+$(OUT_DIR)/index.raw.html: $(SRC_DIR)/index.1