commit ef3aa81
shrub
·
2026-05-17 21:29:15 +0000 UTC
parent ef3aa81
init
+3,
-0
1@@ -0,0 +1,3 @@
2+out/
3+sites/shrub
4+sites/stress
A
Makefile
+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*<.*>\s*$/}@l;if($ok){$r=~s/</</g;$r=~s/>/>/g;$r=~s/"/"/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\">› %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
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
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