From db2a259a18b9247edc23b1128dcfda703858f120 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 21:05:07 -0300 Subject: A one-page A4 cheatsheet, the first file of a Typst docs corpus in docs/typ docs/typ/cheatsheet.typ holds the facts as semantic calls (#key, #btn, #chord, #word, #tag, #addr, #file, #cmd, #doc); docs/typ/style.typ defines those once, drawing them for paged output and emitting HTML elements under --features html; docs/typ/cheatsheet-a4.typ is the page. `zig build cheatsheet` renders it when typst is on PATH. A config.zig test reads every .typ in docs/typ and fails when one names a builtin, key binding or default tag pardes no longer has. Co-Authored-By: Claude Opus 5.5 --- docs/typ/style.typ | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/typ/style.typ (limited to 'docs/typ/style.typ') diff --git a/docs/typ/style.typ b/docs/typ/style.typ new file mode 100644 index 00000000..7cb05e23 --- /dev/null +++ b/docs/typ/style.typ @@ -0,0 +1,101 @@ +// The semantic elements every docs/typ topic uses, and the one place they +// are defined. Content files call them ("this is a key", "this is a +// builtin"); how each looks is decided here, per output: paged outputs +// (the A4 cheatsheet, later the book) draw them, HTML export gives them +// elements and classes for a stylesheet. Page geometry and fonts belong to +// the output wrappers beside this file. + +/// The `atelier` theme (src/themes/atelier.zig): pardes's acme homage, +/// butter paper and blue-green tags. +#let palette = ( + paper: rgb("#fffdeb"), + ink: rgb("#293f35"), + tag-bg: rgb("#e4f0ed"), + tag-fg: rgb("#3d6055"), + tag-name: rgb("#6e4774"), + grip: rgb("#36826b"), + col-grip: rgb("#955815"), + str: rgb("#34743d"), + num: rgb("#9a5810"), + rule: rgb("#95baa2"), +) +#let mono = "DejaVu Sans Mono" + +/// Topic ids for #doc. Each names the document that holds the topic today; +/// as topics move into docs/typ the value becomes the HTML page or chapter. +#let topics = ( + tags: "docs/tags.md", + fs: "docs/fs.md", + config: "docs/config.md", + themes: "docs/themes.md", + keys: "docs/helix-keys.md", + detached: "docs/detached.md", +) + +#let html-out() = target() == "html" +#let span(class, body) = html.elem("span", attrs: (class: class), body) +#let m(s, fill: palette.ink) = text(font: mono, size: 0.92em, fill: fill, s) + +/// A key or a sequence of keys, one cap per space-separated chord. +#let key(k) = context if html-out() { + k.split(" ").map(t => html.elem("kbd", t)).join(" ") +} else { + k.split(" ").map(t => box( + fill: white, stroke: 0.4pt + palette.rule, radius: 1.5pt, + inset: (x: 1.6pt, y: 0pt), outset: (y: 1.4pt), m(t), + )).join(h(1pt)) +} + +/// A mouse button. +#let btn(b) = context if html-out() { span("btn", b) } else { + box( + fill: palette.col-grip, radius: 2pt, inset: (x: 1.6pt, y: 0pt), outset: (y: 1.4pt), + text(font: mono, size: 0.85em, weight: "bold", fill: white, b), + ) +} + +/// A chord: the first button held, the others clicked. +#let chord(..bs) = bs.pos().map(btn).join(text(fill: palette.col-grip)[--]) + +/// A tag word or builtin, as it sits in a tag. +#let word(w) = context if html-out() { span("word", raw(w)) } else { + box(fill: palette.tag-bg, inset: (x: 1.4pt, y: 0pt), outset: (y: 1.4pt), m(w, fill: palette.tag-fg)) +} + +/// A whole tagline: its grip, the path, then its words. +#let tag(words, path: none) = context if html-out() { + html.elem("div", attrs: (class: "tag"), { + if path != none { span("path", path); [ ] } + raw(words) + }) +} else { + block(width: 100%, fill: palette.tag-bg, inset: (x: 2pt, y: 2.4pt), spacing: 0.5em, { + box(fill: palette.grip, width: 0.55em, height: 0.75em, baseline: 0.1em) + h(3pt) + if path != none { m(path, fill: palette.tag-name); h(0.6em) } + m(words, fill: palette.tag-fg) + }) +} + +/// A look address. +#let addr(a) = context if html-out() { span("addr", raw(a)) } else { m(a, fill: palette.tag-name) } + +/// A 9P path. +#let file(p) = context if html-out() { span("file", raw(p)) } else { m(p, fill: palette.str) } + +/// A shell line. +#let cmd(c) = context if html-out() { raw(c, block: true, lang: "sh") } else { + block( + width: 100%, inset: (left: 3pt, y: 1.2pt), spacing: 0.4em, + stroke: (left: 1pt + palette.rule), + text(font: mono, size: 0.85em, fill: palette.ink, c), + ) +} + +/// Where the full story is: a topic id from `topics`, and a section in it. +#let doc(id, section: none) = { + let to = topics.at(id) + if section != none { "#" + section } else { "" } + context if html-out() { link(to)[→ #to] } else { + text(fill: palette.num)[→#m(to, fill: palette.num)] + } +} -- cgit v1.3