# Hiccup → Typst Mapping Reference ## Hiccup Syntax All elements follow standard Hiccup: ```clojure [:tag {attrs} children...] ``` - **tag**: Element type (keyword) - **attrs**: Optional map of attributes - **children**: Content (strings, nested elements, or expressions) ### Element Categories | Category | Examples | Children | |----------|----------|----------| | Content elements | `heading`, `strong`, `emph`, `block` | Display content | | Source elements | `link`, `image`, `bibliography` | `link` has display text; others have none | | List elements | `list`, `enum`, `table` | Each child = one item/cell | | Container elements | `figure` | Single content child (compiled as function arg) | | Math | `math` | Math expression string | | References | `ref`, `cite` | None | | Code | `raw` | Code string to display | | Rules | `set` | None | | Show rules | `show` | A `set` rule as child | ### Reserved Attributes | Attr | Used by | Purpose | |------|---------|---------| | `:src` | `link`, `image`, `bibliography` | Source URL/path | | `:label` | Any element | Makes element referenceable | | `:caption` | `figure` | Figure caption text | | `:target` | `ref` | Label to reference | | `:keys` | `cite` | Citation key(s) | | `:element` | `set`, `show` | Element type to configure | | `:block` | `math`, `raw` | Display as block (default: inline) | | `:lang` | `raw` | Code language for syntax highlighting | ### Examples ```clojure ;; Content element [:heading {:level 2} "Section Title"] ;; Source element with children [:link {:src "https://example.com"} "Click here"] ;; Source element without children [:image {:src "diagram.png" :width "80%"}] ;; List element [:list "First" "Second" "Third"] ;; Container with label [:figure {:caption "Results" :label :fig/results} [:image {:src "chart.png"}]] ;; Math (inline and block) [:math "E = m c^2"] [:math {:block true} "sum_(i=0)^n i"] ;; Reference and citation [:ref {:target :fig/results}] [:cite {:keys [:smith2020 :jones2021]}] ;; Code display [:raw {:lang "python" :block true} "print('hello')"] ;; Set rule [:set {:element :page :margin "2cm"}] ;; Show rule (child is the set rule to apply) [:show {:element :heading} [:set {:element :text :fill "blue"}]] ``` --- ## Audit of Current Tags ### ✅ Valid - Direct Typst Functions | Hiccup | Typst | Notes | |--------|-------|-------| | `[:heading {:level 2} "Text"]` | `#heading(level: 2)[Text]` | ✅ Works | | `[:strong "text"]` | `#strong[text]` | ✅ Works | | `[:emph "text"]` | `#emph[text]` | ✅ Works | | `[:link {:src "url"} "text"]` | `#link("url")[text]` | ✅ Works | | `[:image {:src "path.png" :width "50%"}]` | `#image("path.png", width: 50%)` | ✅ Works | | `[:block {:inset "1em"} ...]` | `#block(inset: 1em)[...]` | ✅ Works | | `[:bibliography {:src "refs.bib"}]` | `#bibliography("refs.bib")` | ✅ Works | ### ❌ Invalid - Don't Exist in Typst | Bad Hiccup | Problem | Fix | |------------|---------|-----| | `[:p "text"]` | No paragraph function | Just use strings, paragraphs are implicit | | `[:item "text"]` | Not a function | Items are args to `list()` | | `[:cell "text"]` | Not a function | Cells are args to `table()` | | `[:div ...]` | HTML, not Typst | Use `[:block ...]` | | `[:span ...]` | HTML, not Typst | Just inline content, or `[:box ...]` | | `[:aside ...]` | Doesn't exist | Use `[:block ...]` with styling | | `[:section ...]` | Doesn't exist | Just use headings | | `[:doc ...]` | Doesn't exist | Top-level is just content | | `[:table-row ...]` | Doesn't exist | Tables are flat | ### ⚠️ Needs Adjustment | Current | Problem | Correct | |---------|---------|---------| | `[:linebreak]` | Function name | `[:linebreak]` → `#linebreak()` ✅ actually works | | `[:h-space "2em"]` | Wrong name | `[:h "2em"]` → `#h(2em)` | | `[:figure [:image ...] [:caption ...]]` | Caption isn't a child | Caption goes in attrs | | `[:code {:lang "py"} "..."]` | Function is `raw` | `[:raw {:lang "python"} "..."]` | ## Corrected Mappings ### Lists Typst `list` takes content blocks as positional args, not `:item` children: ```clojure ;; OLD (wrong) [:list [:item "First"] [:item "Second"]] ;; NEW (correct) [:list "First" "Second"] ;; => #list([First], [Second]) ;; With formatted items [:list [:strong "Important"] "Regular item"] ;; => #list([#strong[Important]], [Regular item]) ;; Nested [:list "Top level" [:list "Nested 1" "Nested 2"]] ``` **Typst output:** ```typst #list( [First], [Second], ) ``` ### Tables Same pattern - cells are positional args: ```clojure ;; OLD (wrong) [:table {:columns 2} [:cell "A"] [:cell "B"] [:cell "1"] [:cell "2"]] ;; NEW (correct) [:table {:columns 2} "A" "B" "1" "2"] ;; => #table(columns: 2, [A], [B], [1], [2]) ;; With formatting [:table {:columns 2} [:strong "Header 1"] [:strong "Header 2"] "Data 1" "Data 2"] ``` **Typst output:** ```typst #table( columns: 2, [#strong[Header 1]], [#strong[Header 2]], [Data 1], [Data 2], ) ``` ### Figures Caption is a named parameter, not a child element: ```clojure ;; OLD (wrong) [:figure [:image "arch.png"] [:caption "Architecture diagram"]] ;; NEW (correct) [:figure {:caption "Architecture diagram"} [:image {:src "arch.png"}]] ;; => #figure(image("arch.png"), caption: [Architecture diagram]) ;; With label for referencing [:figure {:caption "Architecture" :label :fig/arch} [:image {:src "arch.png" :width "80%"}]] ;; => #figure(image("arch.png", width: 80%), caption: [Architecture]) ``` **Typst output:** ```typst #figure( image("arch.png", width: 80%), caption: [Architecture], ) ``` ### Math ```clojure ;; Inline math (default) [:math "x^2 + y^2"] ;; => $x^2 + y^2$ ;; Block/display math [:math {:block true} "sum_(i=0)^n i"] ;; => $ sum_(i=0)^n i $ ``` ### Code/Raw ```clojure ;; Inline code [:raw "let x = 1"] ;; => `let x = 1` ;; Code block with language [:raw {:lang "python" :block true} "print('hello')"] ;; => ```python ;; print('hello') ;; ``` ``` **Typst output:** ```typst #raw("let x = 1") #raw(lang: "python", block: true, "print('hello')") ``` ### References and Citations ```clojure ;; Reference a label [:ref {:target :fig/arch}] ;; => @fig:arch ;; Single citation [:cite {:keys [:smith2020]}] ;; => @smith2020 ;; Multiple citations [:cite {:keys [:smith2020 :jones2021]}] ;; => @smith2020 @jones2021 ``` ### Horizontal/Vertical Space ```clojure [:h "2em"] ;; => #h(2em) [:v "1em"] ;; => #v(1em) ``` ### Page/Document Settings `set` and `show` rules configure document styling. Use `:element` attr to specify the target: ```clojure ;; Set rules (affects everything after) [:set {:element :page :paper "a4" :margin "2cm"}] ;; => #set page(paper: "a4", margin: 2cm) [:set {:element :text :font "New Computer Modern" :size "11pt"}] ;; => #set text(font: "New Computer Modern", size: 11pt) ;; Show rules (child is the set rule to apply) [:show {:element :heading} [:set {:element :text :fill "blue"}]] ;; => #show heading: set text(fill: blue) ``` ### Paragraphs There is no paragraph function. Paragraphs are created by blank lines: ```clojure ;; Just strings, separated by [:parbreak] or blank content "First paragraph." [:parbreak] "Second paragraph." ;; Or in raw mode, just write naturally #t" First paragraph. Second paragraph. " ``` ## Revised Full Example ```clojure (def my-doc [;; Set rules at top [:set {:element :page :paper "a4"}] [:set {:element :text :font "Linux Libertine"}] [:heading {:level 1} "My Paper"] "This is the introduction. We discuss " [:emph "important things"] "." [:parbreak] [:heading {:level 2} "Methods"] "We used the following approach:" [:list "Step one" "Step two" [:strong "Step three (important)"]] [:heading {:level 2} "Results"] "See " [:ref {:target :fig/results}] " and " [:ref {:target :tab/data}] "." [:figure {:caption "Results visualization" :label :fig/results} [:image {:src "results.png" :width "80%"}]] [:figure {:caption "Data table" :label :tab/data} [:table {:columns 2} [:strong "Input"] [:strong "Output"] "1" "1" "2" "4" "3" "9"]] "The equation is " [:math "E = m c^2"] "." [:math {:block true} "integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2"] [:bibliography {:src "refs.bib"}]]) ``` **Compiles to:** ```typst #set page(paper: "a4") #set text(font: "Linux Libertine") #heading(level: 1)[My Paper] This is the introduction. We discuss #emph[important things]. #heading(level: 2)[Methods] We used the following approach: #list( [Step one], [Step two], [#strong[Step three (important)]], ) #heading(level: 2)[Results] See @fig:results and @tab:data. #figure( image("results.png", width: 80%), caption: [Results visualization], ) #figure( table( columns: 2, [#strong[Input]], [#strong[Output]], [1], [1], [2], [4], [3], [9], ), caption: [Data table], ) The equation is $E = m c^2$. $ integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2 $ #bibliography("refs.bib") ``` ## Typst Gotchas Important Typst behaviors the compiler must handle: 1. **Math variable spacing**: In math mode, adjacent letters like `mc` are parsed as a single variable name. To represent multiplication, add spaces: `$m c^2$` not `$mc^2$`. The compiler should insert spaces between single-letter variables. 2. **Referenceable tables**: Tables cannot have labels directly attached. To make a table referenceable with `@label`, wrap it in a `#figure()`: ```typst #figure( table(...), caption: [...], )