From: Kai Moritz Date: Sat, 6 Jun 2026 11:22:47 +0000 (+0000) Subject: Document Astro design decisions in CLAUDE.md; fix branch name X-Git-Url: https://juplo.de/gitweb/?a=commitdiff_plain;h=8f598f9366cc93437a2e3c2f33471a70b2047fd5;p=website Document Astro design decisions in CLAUDE.md; fix branch name 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 --- diff --git a/CLAUDE.md b/CLAUDE.md index 769c7191..69a0c795 100644 --- 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 `` 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.