]> juplo.de Git - website/commitdiff
Document Astro design decisions in CLAUDE.md; fix branch name
authorKai Moritz <kai.milan.moritz@googlemail.com>
Sat, 6 Jun 2026 11:22:47 +0000 (11:22 +0000)
committerKai Moritz <kai.milan.moritz@googlemail.com>
Sat, 6 Jun 2026 13:20:22 +0000 (13:20 +0000)
Adds a 'Astro-Implementierung: Design- und Architektur-Entscheidungen'
section that records all non-trivial decisions made during the migration:
- CSS pre-compilation via separate sass step (not Astro built-in)
- Split taxonomy URL structure matching Hugo's output
- BlogNav as an explicit TypeScript state machine (vs. Hugo's recursive
  template partial)
- URL compatibility via frontmatter 'url' field
- Word count / reading time via inline string manipulation
- Summary extraction from raw Markdown body
- .html suffix routing via double-extension file naming
- Standalone pages without navigation (documented as open issue)

Also corrects the branch name in Arbeitsregeln from content--via-hugo
to content--via-astro.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLAUDE.md

index 769c71916213ad970719d50db66a6ff4e907028e..69a0c79566b702a8fed40e509bfec0fe14b613b2 100644 (file)
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -5,7 +5,7 @@ Diese Datei enthält Anweisungen für Claude Code (claude.ai/code) beim Arbeiten
 ## Arbeitsregeln
 
 - Wenn du nicht explizit anders angewiesen wirst, arbeitest du nur innerhalb dieses Repositories und des Theme-Submodules.
-- Du arbeitest immer auf dem bereits ausgecheckten Branch `content--via-hugo`.
+- Du arbeitest immer auf dem bereits ausgecheckten Branch `content--via-astro`.
 - Ab und zu bin ich ungenau oder bringe Details durcheinander. Wenn du den Eindruck hast, dass etwas nicht passt oder Details fehlen, frage immer direkt nach.
 - Für alle Aufgaben gilt: du kannst und sollst Nachfragen stellen, bevor du mit der Umsetzung beginnst, wenn das sinnvoll ist.
 
@@ -105,3 +105,107 @@ Der `exampleSite`-Ansatz im Theme dient zum isolierten Testen des Themes unabhä
 Das Theme ist funktional (Menü, Breadcrumb, Taxonomien, Blog-Archiv, Syntax-Highlighting, responsive Layout). Die Template-Implementierung enthält noch Copy-paste-Duplikate, die schrittweise konsolidiert werden sollen. Größte offene Aufgabe: Den `menu`-Block aus den 7+ Einzel-Templates in ein parametrisiertes Partial konsolidieren.
 
 Bekannte Baustellen und ihre Priorität sind in der Analyse vom 2026-06-03 dokumentiert.
+
+---
+
+## Astro-Implementierung: Design- und Architektur-Entscheidungen
+
+Dieser Abschnitt dokumentiert die technischen Entscheidungen des Astro-Branches (`content--via-astro`). Er unterscheidet zwischen Entscheidungen, die Astro-Standard folgen, und solchen, bei denen bewusste Abweichungen getroffen wurden.
+
+### Standard-Entscheidungen (Astro Best Practices)
+
+**Statische Site-Generierung (SSG)**
+Astro erzeugt beim Build statische HTML-Dateien. Der `output`-Mode ist nicht explizit konfiguriert, da `'static'` Astros Default ist.
+Quelle: https://docs.astro.build/en/basics/rendering-modes/
+
+**Content Collections mit glob-Loader**
+Blog-Artikel in `src/content/blog/` werden über Astros Content Collections API eingebunden. Die Konfiguration in `src/content.config.ts` nutzt den `glob()`-Loader, der alle `.md`-Dateien im Verzeichnis einliest und Schema-Validierung (Zod) für Frontmatter-Felder ermöglicht.
+Quelle: https://docs.astro.build/en/guides/content-collections/
+
+**Dateibasiertes Routing**
+Jede Datei in `src/pages/` ergibt eine Route. Dynamische Routen (`[year].astro`, `[term].astro`) implementieren `getStaticPaths()`, um alle Pfade zur Build-Zeit zu erzeugen.
+Quelle: https://docs.astro.build/en/guides/routing/
+
+**Named Slots in Layouts**
+`BaseLayout.astro` definiert benannte Slots (`breadcrumb`, `article`, `menu`, `marginalcontent`, `footerlinks`), die von Seiten mit `<Fragment slot="…">` befüllt werden. Dies spiegelt direkt Hugo's `{{ define "block" }}`-Pattern.
+Quelle: https://docs.astro.build/en/basics/layouts/
+
+**TypeScript im Frontmatter**
+Alle `.astro`-Dateien nutzen TypeScript im `---`-Frontmatter. Astro kompiliert das automatisch, ohne separate Build-Konfiguration.
+Quelle: https://docs.astro.build/en/guides/typescript/
+
+---
+
+### Eigene Entscheidungen (nicht Astro-vorgegeben)
+
+#### CSS: Separater SCSS-Build statt Astro-Integration
+
+**Was:** SCSS wird über einen separaten `sass`-CLI-Aufruf kompiliert (`npm run build:css`), nicht über Astros eingebaute SCSS/Vite-Integration. Das kompilierte CSS landet in `public/css/` und ist im Git eingecheckt.
+
+**Warum:** Das SCSS stammt aus dem Hugo-Theme-Repository (`themes/hugo-juplo`). Eine Integration in Astros Asset-Pipeline hätte entweder Copy-paste oder komplexe Symlink-Logik erfordert. Der separate Build-Schritt hält die SCSS-Quellen im Theme-Repo und das kompilierte Ergebnis im Website-Repo — analog zur Hugo-Situation, wo das Theme die SCSS-Quellen besitzt und der Build das CSS erzeugt. Der `prebuild`/`predev`-Hook in `package.json` stellt sicher, dass CSS immer aktuell ist.
+
+**Konsequenz:** SCSS-Änderungen erfordern `npm run build:css` oder einen Dev-Server-Neustart. HMR funktioniert für SCSS nicht automatisch.
+
+---
+
+#### Taxonomy-URL-Struktur: Split zwischen Index und Term
+
+**Was:** Die Taxonomie-Seiten sind auf zwei URL-Ebenen verteilt:
+- Index-Seiten: `/blog/categories/` und `/blog/tags/` (unter dem Blog-Pfad)
+- Term-Seiten: `/categories/TERM/` und `/tags/TERM/` (direkt unter Root)
+
+**Warum:** Diese Struktur ist identisch mit dem Hugo-Output. Hugo legt Taxonomie-Listenseiten (`/blog/categories/`) unter dem konfigurierten `taxonomyTerms`-Pfad ab, während Term-Seiten (`/categories/kafka/`) direkt unter dem Taxonomie-Namen generiert werden. Die Beibehaltung dieser Struktur ist notwendig für URL-Kompatibilität mit bestehenden Links und SEO.
+
+---
+
+#### BlogNav: Explizite State-Machine statt Template-Rekursion
+
+**Was:** `BlogNav.astro` berechnet den Navigationszustand vollständig in TypeScript-Variablen (`onBlog`, `onArchive`, `onCategoriesIndex`, `onCategoryTerm`, etc.) und rendert das HTML anhand dieser Flags.
+
+**Warum:** Hugo's `menu/tree.html` ist ein rekursives Partial, das den Baum traversiert und dabei Hugo-interne Konzepte wie `.IsDescendant`, `.Ancestors`, `.Eq` nutzt. Diese existieren in Astro nicht. Statt einer allgemeinen Lösung wurde für den Blog-Ast (der einzige aktuell implementierte Ast) eine explizite State-Machine gewählt, die die konkreten Seitentypen des Blogs kennt und die entsprechenden CSS-Klassen (`off`, `sub`, `selected`, `active`) direkt ableitet.
+
+**Entscheidungsgrundlage:** Die `off`-Klassen wurden durch Analyse des Hugo-HTML-Outputs für alle relevanten Seitentypen ermittelt (Homepage, `/blog/`, Archiv-Seiten, Jahres-Seiten, Blog-Posts, Categories/Tags-Index, Categories/Tags-Term).
+
+**Wichtige Regel:** Ein `li.s.off`-Element ist via CSS (`#submenu li.s.off { display: none }`) ausgeblendet. Ein `ul.s` (ohne `active`) blendet sich selbst nicht aus — nur `li.s.nav-leaf` und `li.s.off` sind hidden. Diese CSS-Regel war entscheidend für die korrekte Implementierung der Navigation.
+
+---
+
+#### URL-Kompatibilität via `url`-Frontmatter-Feld
+
+**Was:** Blog-Artikel können ein `url`-Feld im Frontmatter setzen (z.B. `url: /mein-alter-slug/`). Astro verwendet dieses, um die kanonische URL zu erzeugen, statt des Dateinamens.
+
+**Warum:** Hugo generierte Blog-Post-URLs aus dem `url`-Frontmatter-Feld oder aus dem Slug, teilweise flach unter `/` (nicht unter `/blog/`). Um keine URLs zu brechen, wird `post.data.url ?? '/${post.id}/'` als Postlink verwendet. Die `getStaticPaths()`-Funktion in `[slug].astro` leitet die Route ebenfalls aus diesem Feld ab.
+
+---
+
+#### Word Count und Reading Time: Manuelle Berechnung aus Markdown-Body
+
+**Was:** Wortanzahl und Lesezeit werden in Hilfsfunktionen `wordCount()` und aus `Math.ceil(wc / 200)` berechnet, indem Code-Blöcke aus dem Markdown-Body entfernt und der Rest auf Whitespace gesplittet wird.
+
+**Warum:** Astro stellt keine eingebaute Word-Count-API bereit. Alternativen wären Remark-Plugins, die beim Build laufen. Die Inline-Berechnung ist einfacher und ausreichend genau. Code-Blöcke werden herausgefiltert, weil sie die Lesezeit verzerren würden. Die 200-wpm-Rate entspricht Hugo's Standard (`reading_time_minutes = words / 200` gerundet aufwärts).
+
+---
+
+#### Summary-Extraktion: Erste Nicht-Code-Paragraph bis 350 Zeichen
+
+**Was:** `getSummary(body)` extrahiert eine Vorschau aus dem Markdown-Body, indem Code-Blöcke, HTML-Tags, Shortcodes, Links, Markdown-Formatierung und Überschriften entfernt werden und dann der erste Paragraph mit mindestens 20 Zeichen und maximal 350 Zeichen zurückgegeben wird.
+
+**Warum:** Hugo nutzt automatisch den ersten Paragraph als Summary. In Astro gibt es dafür keine eingebaute Funktion — bei Content Collections ist `post.body` der rohe Markdown-String. Statt eines Remark-Plugins wurde eine String-Manipulation gewählt, die für den vorhandenen Content ausreichend ist.
+
+---
+
+#### `.html`-Endungen für Standalone-Seiten
+
+**Was:** Seiten wie About, Impressum und Datenschutz haben `.html`-Endungen in der URL (`/about.html`). In Astro wird das durch Benennung der Datei als `about.html.astro` erreicht.
+
+**Warum:** Diese URLs sind historisch aus dem WordPress-Import gewachsen und wurden in Hugo explizit mit `url: /about.html` beibehalten. Änderungen würden externe Links und Suchmaschinen-Indizes brechen.
+
+---
+
+#### Standalone-Seiten ohne Navigation (Offener Punkt)
+
+**Was:** Die Seiten `about.html`, `impressum.html` und `datenschutz.html` haben keinen `menu`-Slot befüllt und zeigen daher keine Seitennavigation.
+
+**Warum:** Hugo zeigt auf Standalone-Seiten eine vollständige Site-Navigation (Blog-Baum + Projects-Baum + About-Baum). Die Implementierung dafür würde eine `SiteNav`-Komponente erfordern, die alle Sektionen kennt — inklusive der Projects-Seiten mit deren `current`-Flag-Logik. Das ist deutlich aufwendiger als `BlogNav` und wurde für die Migration zurückgestellt.
+
+**Zukünftige Aufgabe:** Eine `SiteNav.astro`-Komponente, die alle Hauptsektionen rendert und die `menu/tree.html`-Logik für Projects (only show `current: true` pages unless on path) repliziert.