t

tidymind

@preview

Draw tidy horizontal mind maps with CeTZ

v0.2.0
MIT

Package Information

Last Updated
Minimum Typst Version
0.14.0
Categories
visualization

1. Get the package

Download the package using the TPIX CLI:

tpix get @preview/tidymind:0.2.0

2. Import in your Typst file

Add this to your .typ file:

#import "@preview/tidymind:0.2.0": *

Version History

0.2.00.14.0
780d611c83e1...
0.1.10.14.0
5f7fe42c4337...

tidymind

Horizontal mind map diagrams for Typst, built on
CeTZ. Every node is measured before
the layout runs, so long labels never overlap — the common failure of
fixed-spacing tree drawers.

A mind map with a filled root and rounded, colored nodes

Usage

#import "@preview/tidymind:0.2.0": mindmap, node

#mindmap(node([Root],
  node([Branch A], node([A1]), node([A2])),
  node([Branch B]),
))

node(content, ..children) builds a tree node; content is the label and the
remaining positional arguments are its children (each one another node(...) or
raw content). A raw dictionary (content: .., children: (..)) is also accepted.

Long labels

This is the case that pushed the package into existence. Node sizes come from
Typst's measure, so a label that wraps reserves the vertical band it actually
needs — at any depth, with no manual offsets.

Two long labels wrapped at node-max-width, neither overlapping the other

Styles

style: "boxed" (the default) draws every node as a rounded box, the root
filled with its branch color.

style: "outline" drops the boxes entirely: the root becomes a heading over a
baseline rule, each first-level branch a label resting on a rule in its own
color, and everything deeper is plain text. Hierarchy comes from size, weight
and color instead of from frames — useful when the map sits inside a document
and boxes would fight with the surrounding text.

The same tree in the outline style, with no boxes around any node

Roles and branch colors

A node can carry an emphasis — its role — and a branch index that overrides
the color it would inherit from its position. Both are given by name: the
document says what a node means, and the package resolves the color.

#mindmap(
  node([SQL privileges],
    node([GRANT],
      node([Idempotent], emphasis: "definition"),
      node([Cascades to dependents], emphasis: "warning"),
    ),
    node([REVOKE], branch: 5,
      node([RESTRICT is the default], emphasis: "highlight"),
    ),
  ),
  style: "outline",
)

A map whose leaves are colored by role: definition, warning, highlight and example

Options

Option Default Meaning
style "boxed" "boxed" or "outline" (see above)
palette 6 colors color per first-level branch, cycled
font "Inter" label font, or a fallback list
text-size 9pt base label size; the root and first level scale up from it
node-max-width 6cm max width before a label wraps
max-depth 6 prune nodes deeper than this
h-gap 40pt horizontal gap between levels
v-gap 10pt minimum vertical gap between siblings
ink (strong, soft) label colors; partial dictionaries merge over the defaults
emphasis-colors 4 roles highlight, warning, definition, example

Labels take arbitrary Typst content, so markup and emoji work — pass a color
emoji font in the font fallback list to get the second one:

#mindmap(node([Git: #strong[what gets graded]], node([🥇 #strong[Remote sync] (35%)])),
  font: ("Inter", "Noto Color Emoji"))

How it works

The layout is a tidy tree by subtree extent: every subtree reserves a vertical
band equal to the sum of its children's bands (or its own height, if a leaf), and
the parent is centered within that band. Because sibling subtrees occupy disjoint
bands, nodes never overlap — at any depth. It runs in O(n), in two passes: one
up the tree to size the bands, one down to place the nodes.

Node sizes come from Typst's measure, so a band accounts for the real rendered
size of each (possibly wrapped) label. Measuring and drawing go through a single
description of the node body (src/style.typ), which is what keeps an edge
landing exactly on the node it points at.

Examples

Every file under examples/ compiles on its own. Files named
visual_* produce the images above; files named _assert_* exercise the logic
through #assert, so compiling them is the test suite.

sh examples/render.sh    # runs the asserts, then regenerates img/
Example What it covers
visual_shallow a root with three leaves
visual_deep several levels of nesting
visual_many_siblings vertical spacing under pressure
visual_long_labels labels wrapping at node-max-width
visual_outline the "outline" style
visual_emphasis roles and branch overrides
visual_markdown_emoji markup and emoji in labels
visual_single a lone root
visual_empty empty labels

Changelog

0.2.0 — adds style: "outline", the branch and emphasis attributes on
node, and the ink / emphasis-colors options. The default output is
unchanged: style: "boxed" renders exactly what 0.1.1 rendered.

0.1.1 — long labels wrap instead of overflowing their measured width.

License

MIT