Python-Projekte werden häufig mit Material for MkDocs dokumentiert. Das habe ich auch lange genutzt, nach einem Streit zwischen den Entwickler:innen splittern diese sich auf in Zensical, ProperDocs, MkDocs 2. Nun muss ich mir überlegen, was ich für meine Projekte in Zukunft nutzen möchte und habe mich noch nicht abschließend entscheiden können.

Irgendwer hatte mir mal Material for MkDocs gezeigt, seitdem habe ich das total gerne genutzt. Es ist einfach in ein Python-Projekt einzufügen, man schreibt einfach Markdown. Der Upload auf GitHub Pages ist sogar schon integriert. Das macht richtig Spaß. Ich habe es dann für Geo Activity Playground und Vigilant Crypto Snatch genutzt.

Nun wollte ich es auch für meine Project Euler Solutions nutzen. Dann allerdings bekam ich die Warnung, dass das Projekt auseinanderfallen würde. Ich habe noch ein bisschen im Internet recherchiert und dann den schönen Blogartikel The Slow Collapse of MkDocs gefunden.

Die Gemengelage ist wohl kompliziert. Material for MkDocs ist ein Plugin für MkDocs, das allerdings ziemlich viel macht. Es ist nicht ganz ein Total Conversion Mod, aber es ändert das Theme, fügt eine Suchfunktion hinzu und macht noch ein paar weitere Dinge. Das nackte MkDocs hatte ich mir einmal angeschaut, aber das fand ich echt öde.

Nun hat Mia Kimberly Christie, Initiatorin von MkDocs, sich länger nicht mehr um das Projekt gekümmert. Einige Zeit hat Waylan Limberg sich um das Projekt gekümmert, es gab aber Streit mit Oleh Prypin, der auch beigetragen hat. Limberg hat Beiträge von Prypin abgelehnt, das konnte letzterer allerdings nicht verstehen. Es gab viel Knatsch. Wirklich erwachsen verhalten hatte sich Martin Donath, die Hauptperson hinter Material for MkDocs.

Am Ende haben sich alle zerstritten, es gibt kein gemeinsames Projekt mehr. Nun hat sich das ganze in mehrere Teile zerlegt:

  • Christie baut an MkDocs 2.0, einem nicht kompatiblem “Nachfolger”, der wohl eher ein neues Projekt ist. Das ganze wird unter Ausschluss der Gemeinschaft entwickelt, vorgeblich um Drama zu vermeiden. Letztlich wird das aber keine neue Basis für Material for MkDocs sein, da kein Plugin-System vorgesehen ist.
  • Prypin pflegt nun ProperDocs, einen Fork von MkDocs 1, der weiterhin mit allen Plugins kompatibel sein soll. Das ergibt auch Sinn, schließlich hat er MkDocs zuletzt auch gepflegt.
  • Donath baut nun eine eigene Basis. Zensical erzeugt HTML fast wie Material for MkDocs, ist allerdings komplett neu in Rust geschrieben. Es ist auch mit der mkdocs.yml kompatibel und scheint mir der offensichtliche Nachfolger zu sein.

Wohin gehe ich?

Mir stellt sich natürlich die Frage, wohin ich jetzt mit meiner Dokumentation gehe. Das schöne an Markdown ist die Kompatibilität. An sich ist es nicht wichtig, welches Programm die Markdown-Dateien in HTML konvertiert und mit einer Navigationsleiste anreichert. Am Ende stehen da meine Inhalte und das ist alles gut für Leser:innen nutzbar. Es ist mehr eine Geschmackssache.

Die technische Basis der Software ist nicht sehr wichtig. MkDocs ist in Python geschrieben und daher nutzt es Python-Markdown, das einige nette Erweiterungen hat. Diese Erweiterungen gibt es aber auch in anderen Programmiersprachen, dann leider etwas anders. Markdown ist eben nicht komplett standardisiert, zumindest sind diese Erweiterungen nicht standardisiert. reStructuredText hat da mehr Möglichkeiten, aber ich mag das inzwischen einfach nicht mehr nutzen.

Sollte ich etwas anpassen wollen, ist die Template Engine interessant. In Python gibt es Jinja-Templates, mit denen kann ich gut arbeiten. Es gibt auch noch Mako, damit habe ich deutlich weniger Erfahrung. Andere Programmiersprachen haben ganz andere Template-Engines, die aber auch alle mehr oder weniger ähnlich funktionieren. Das brauche ich allerdings nur, wenn ich da wirklich etwas anpassen möchte.

Die Programmiersprache hat ein bisschen Einfluss auf die Geschwindigkeit; Zensical in Rust ist sicherlich deutlich schneller als MkDocs in Python. Da ich meine Dokumentation allerdings nicht ständig neu erzeuge und sie nicht groß ist, ist mir das nicht so wichtig.

Interessanter ist eher, ob es einen Entwicklungs-Server gibt, mit dem ich mir das anschauen kann. Das haben aber auch viele derartige Programme schon mit drin.

Je nach Projekt mag ich noch mathematische Formeln mit MathJaX oder KaTeX nutzen, das wird allerdings meist auch unterstützt.

Experimente

Da ich bisher Material for MkDocs genutzt habe, ist das meine Vergleichsbasis. Die Dokumentation von Geo Activity Playground habe ich damit einmal erzeugt, siehe GAP Doku mit Material for MkDocs.

In der Diskussion mit KI kam VitePress auf. Davon hatte ich vorher noch nichts gehört. Da es aus dem JavaScript-Universum kommt, habe ich da auch eigentlich keinerlei Anknüpfungspunkte. Allerdings machte das erzeugte HTML einen schönen Eindruck auf mich. Daher habe ich die KI meine MkDocs-Konfiguration portieren lassen und ich konnte es einmal ausprobieren. Hier auch die Demo: GAP Doku mit VitePress. Ich habe in meinem Projekt jetzt allerdings auch eine Abhängigkeit auf NPM und eine packages.json und eine packages-lock.json.

Initial wollte ich das ganze MkDocs-Universum hinter mir lassen. Mir erschien das nach viel zu viel Drama, davon wollte ich einfach weit weg. Bei genauerer Betrachtung scheint mir Donath aber weiterhin vernünftig zu sein, daher habe ich mir Zensical einmal angeschaut und meine Dokumentation auch damit einmal gebaut. Das kann man sich unter GAP Doku mit Zensical anschauen.

Grundlegend sind die ja alle gleich: Inhalte in der Mitte, links die Navigation, rechts das Inhaltsverzeichnis innerhalb der aktuellen Seite. Von muss man das nicht überdenken, wenn die Anforderungen eher gering sind.

Konfigurationsdatei

Bei Material for MkDocs nutzt man die mkdocs.yml. Und die hat in meinem Projekt die explizite Navigation:

nav:
  - index.md
  - Installation:
    - install-on-linux.md
    - install-on-windows.md
    - install-on-macos.md
    - add-local-bin-to-path.md
  - Getting Started:
    - create-a-base-directory.md
    - starting-the-webserver.md
    - record-activities.md

Das ist einfach zu pflegen. Mir gefällt auch, wie die Seitentitel aus der obersten Überschrift der jeweiligen Markdown-Datei extrahiert werden.

Man kann dann noch ein paar Dinge anpassen. Ich habe das Farbschema, das Umschalten zwischen hell und dunkel sowie die dauerhaft ausgeklappten Navigationsabschnitte. Das geht mit relativ wenig Konfiguration:

theme:
  features:
    - navigation.sections
  logo: public/logo-2.png
  name: material
  palette:
    - scheme: default
      primary: indigo
      accent: indigo
      toggle:
        icon: material/weather-night
        name: Switch to dark mode
      media: "(prefers-color-scheme: light)"
    - scheme: slate
      primary: amber
      accent: amber
      toggle:
        icon: material/weather-sunny
        name: Switch to light mode
      media: "(prefers-color-scheme: dark)"

Zensical akzeptiert die Konfiguration von MkDocs einfach direkt, da musste ich überhaupt nichts anpassen. Das war schon ziemlich praktisch.

Bei VitePress musste ich das anpassen. Dank KI musste ich mich nicht um die Konfiguration kümmern. Das ist auch gut so, weil das nämlich eine TypeScript-Datei ist. Hier etwas gekürzt:

export default {
  title: 'Geo Activity Playground',
  description: 'View recorded outdoor activities and derive insights from your data.',
  base: '/geo-activity-playground/',
  themeConfig: {
    logo: '/logo-2.png',
    nav: [
      { text: 'Home', link: '/' },
      { text: 'Get Help', link: '/get-help' },
      { text: 'Changelog', link: '/changelog' },
    ],
    sidebar: [
      { text: 'Home', link: '/' },
      {
        text: 'Installation',
        items: [
          { text: 'Install on Linux', link: '/install-on-linux' },
          { text: 'Install on Windows', link: '/install-on-windows' },
          { text: 'Install on macOS', link: '/install-on-macos' },
          { text: 'Add Local Bin to PATH', link: '/add-local-bin-to-path' },
        ],
      },
      { text: 'Get Help', link: '/get-help' },
      { text: 'Changelog', link: '/changelog' },
    ],
    socialLinks: [
      { icon: 'github', link: 'https://github.com/martin-ueding/geo-activity-playground' },
    ],
    outline: {
      level: [2, 3],
    },
    search: {
      provider: 'local',
    },
  },
}

Also auch nicht so wild, eigentlich ist das vor allem JSON. Man muss die Seitentitel selbst angeben, die werden nicht extrahiert. Das ist etwas schade, das erzeugt Inkonsistenz und zusätzliche Arbeit.

Man kann dann noch die Social-Links anpassen, das geht bei beiden ziemlich einfach. Also letztlich nimmt sich das nicht so viel. Zensical unterstützt ein paar mehr Icons, aber an sich ist das alles möglich.

Tief im Kaninchenbau

Rational ist soweit klar: Eigentlich ist es egal, welches ich davon nutze. Die Dokumentation sieht grob gleich aus. Man kann sie vernünftig nutzen und lesen. Ich kann sie automatisiert bauen. Vor allem kann ich aber auch jederzeit wechseln, weil meine eigentlichen Inhalte ja mit allem kompatibel sind.

Aber manchmal sind es gerade diese scheinbar unwichtigen Details, bei denen ich dann viel zu lange darüber nachdenke. Wenn man ehrlich ist, gibt es Static Site Generators wie Sand am Meer. Letztlich gibt es in jeder Programmiersprache je mindestens ein Programm für Dokumentation, generelle Webseiten oder Blogs.

Durchaus häufiger empfohlene Alternativen wären noch Docusaurus, Astro, mdBook, Sphinx, Quarto, Docsy. Die kann man sich alle noch anschauen, aber irgendwie verschwindet da meine Begeisterung. Aber warum habe ich mir dann ausgerechnet VitePress angeschaut und mag es optisch bisher am besten?

mdBook habe ich kurz angeschaut, allerdings gefällt mir die Optik nicht so ganz. Das ist aber eher für ein ruhigeres Buch gedacht als für eine Dokumentations-Seite. Von daher passt es hier auch nicht so gut.

Kleine Unterschiede

Bei Zensical finde ich die Schrift auf meinem Monitor etwas zu groß. Bei VitePress ist die Schrift nur 14 (und nicht 16) Pixel hoch. Also das könnte man wohl ändern. Und aufgrund der Standardschriftgröße das Projekt auszusuchen zeigt eigentlich, wie beliebig es bei mir ist.

Schaut man sich die Dinger auf einem Mobilgerät an, so hat Zensical die Navigationsabschnitte dort eingeklappt. Das ist auf einem Mobilgerät in der Tat etwas praktischer; man muss zwar zweimal tippen, dafür weniger wischen.

Mobil hat VitePress auch zwei Navigationsleisten. In der oberen steht Titel, Suche und das weitere Menü mit Links, Social-Symbolen und dem Hell-Dunkel-Umschalter. In der unteren ist einmal die Seitenleiste links, rechts ist das lokale Inhaltsverzeichnis. Zensical hat nur eine Navigationsleiste, dort ist die globale Navigation, der Titel, der Hell-Dunkel-Umschalter und die Suchfunktion drin. Das lokale Inhaltsverzeichnis ist mit einem Icon realisiert, das unten rechts über dem Inhalt schwebt.

Auf meinem älteren Android-Handy mit Firefox ruckelt die Seitenleiste von Zensical etwas, die von VitePress allerdings nicht.

Da gibt es keinen klaren Gewinner, finde ich.

Entscheidung?

An sich geht es mit Zensical nahtlos weiter. Ich muss letztlich nichts umstellen. Ich habe keine Abhängigkeiten auf NPM-Pakete in meinem Python-Projekt.

Aber irgendwie ist VitePress auch hübsch. Dafür hat es dann wieder andere Nachteile, wie die zwei Navigationsleisten auf dem Mobilgerät.

Zensical:

  • Direkt in uv integrierbar, da es in Rust geschrieben und per Python Wheel ausgeliefert wird.
  • Extrahiert die Titel direkt aus den Seiten.
  • Nur eine Navigationsleiste, finde ich eingängier.
  • Unterstützt Python Markdown Erweiterungen, falls ich die mal brauchen sollte.

VitePress:

  • Schrift ist etwas kleiner, dadurch sieht es nicht so »laut« auf meinem Hauptrechner aus.
  • Ruckelt auf meinem Mobilgerät nicht.
  • Kein Overlay für das lokale Inhaltsverzeichnis, das einerseits stört und andererseits erstmal übersehen werden kann.
  • Kommt direkt als One-Page-App, sodass die Seitenleiste nicht bei Navigation springt. Selbst wenn ich bei Zensical bei theme.features den Punkt navigation.instant hinzufüge, klappt das dort nicht so schön.

Aus einem Bauchgefühl hatte ich zuerst VitePress ausprobiert und direkt für das Projekt eingerichtet. Damit ist die schwere Arbeit ja eigentlich schon gemacht. Aber vielleicht stelle ich doch noch auf Zensical um. Ich bin einfach noch unentschlossen.