Blog
On this page
Blog
Ein Blog ist eine weitere Convention-over-Configuration-Funktion, in
derselben Form wie
Versionen/i18n oder der
Tags-Index - lege Beiträge unter
docs/blog/posts/ ab, und BX Sites baut /blog/ (paginiert), eine
Kategorie-Seite pro Kategorie, eine Jahresarchiv-Seite pro Kalenderjahr,
eine Autoren-Seite pro Autor, einen RSS-Feed pro Kategorie plus einen fĂĽr
den gesamten Blog, und eine /blog/stats/-Seite - ganz ohne
Konfiguration. Ein Projekt ohne docs/blog/posts/-Ordner hat schlicht
keinen Blog - sonst ändert sich nichts.
Einen Beitrag schreiben
Jede .md-Datei unter docs/blog/posts/, in beliebiger Tiefe, ist ein
Beitrag - Unterordner sind vollständig optional und dienen nur deiner
eigenen redaktionellen Ăśbersichtlichkeit. Ein flacher Ordner funktioniert
für eine Handvoll Beiträge einwandfrei; sobald du in die Hunderte gehst,
hält das Ablegen von Beiträgen unter docs/blog/posts/2026/ (oder
docs/blog/posts/2026/03/, oder jedem beliebigen Schema) den Dateibaum
deines Editors ĂĽbersichtlich, ohne etwas umzubenennen oder eine
Frontmatter-Datumspräfix-Konvention anzufassen. Nichts davon beeinflusst
die gebaute Website - die Sortierreihenfolge eines Beitrags, sein
Jahresarchiv und seine URL (blog/<slug>/) werden allein aus der
Frontmatter abgeleitet, nie daraus, wo die Datei zufällig liegt, sodass
der Ordner eines Beitrags und sein tatsächliches date immer
unabhängig voneinander sein dürfen:
docs/blog/posts/
├── hello-world.md (flach ist auch in Ordnung)
├── 2026/
│ ├── announcing-2-0.md
│ └── 03/
│ └── a-deep-dive.md
Frontmatter, fĂĽr jeden Beitrag, egal wo er abgelegt ist:
---
title: Announcing BoxLang 2.0
date: 2026-08-15
authors: [lmajano]
categories: [Releases]
tags: [boxlang, release]
summary: A faster runtime, a smaller footprint, and a few surprises.
image: assets/blog/boxlang-2-cover.png
---
A short intro paragraph or two.
<!-- more -->
The rest of the post - everything below the `<!-- more -->` marker is left
out of the excerpt shown on `/blog/` and category pages, but still renders
in full on the post's own page.
date(erforderlich) - alles, was BX Sites parsen kann (2026-08-15, oder ein vollständiges Datum mit Uhrzeit). Legt die eigene Sortierreihenfolge des Beitrags fest (neueste zuerst) sowie sein<pubDate>/article:published_time.authors- eine Liste von IDs, die zu Einträgen indocs/blog/authors.ymlpassen, oder ein schlichter Name ohne passenden Eintrag (wird als nicht verlinkter Text gerendert, statt den Build scheitern zu lassen - praktisch für einen einmaligen Gastbeitrag).categories- die eigene Taxonomie eines Beitrags, jede erhält ihre eigene/blog/category/<slug>/-Seite (und ihren eigenen/blog/category/<slug>/feed.xml-RSS-Feed - siehe Feed). Unabhängig vontagsunten.tags- dieselbe websiteweitetags-Frontmatter, die jede andere Seite bereits hat (siehe Erste Schritte)- die Tags eines Beitrags werden als Badges gerendert und fließen in
den Haupt-
/tags/-Index ein, zusammen mit jeder anderen getaggten Seite.
- die Tags eines Beitrags werden als Badges gerendert und flieĂźen in
den Haupt-
summary- ein einzeiliger Auszug, gezeigt auf/blog// Kategorie-Seiten und im RSS-Feed, verwendet, wenn ein Beitrag keine<!-- more -->-Markierung hat. Ohne beides fällt BX Sites auf eine reine Klartext-Kürzung des eigenen Textkörpers des Beitrags zurück.image- ein Beitragsbild (ein zudocs/assets/-relativer Pfad, oder eine vollständige URL) - gezeigt oben im Beitrag und als Thumbnail auf jeder Listen-/Kategorie-Card. Wird außerdem das eigeneog:image/die Twitter-Card des Beitrags, sofernogImagedas nicht separat überschreibt. Ein zudocs/assets/-relatives Bild (und der eigeneavatareines Autors, unten) erhält dieselbe responsive<picture>-/srcset-/WebP-Behandlung wie jedes andere Bild unterdocs/assets/- siehe Bilder.slug- überschreibt das URL-Segment (/blog/<slug>/) - standardmäßig aus dem Dateinamen abgeleitet.draft: true- schließt den Beitrag von einem echtenbxSites buildvollständig aus.bxSites servezeigt ihn trotzdem an (mit einem sichtbaren "🚧 Entwurf"-Banner auf dem Beitrag selbst und einer Card mit gestricheltem Rand überall, wo er gelistet wird), sodass du einen Entwurf lokal Korrektur lesen kannst, bevor er fertig ist - siehe Entwürfe ansehen.
Jeder andere Seiten-Frontmatter-SchlĂĽssel, der bereits in
Erste Schritte dokumentiert ist
(icon, description, ogImage, toc), funktioniert auch auf einem
Beitrag.
Beitragsbilder und andere Blog-Assets
docs/assets/blog/ ist nichts Besonderes, außer ein gewöhnlicher
Unterordner von docs/assets/ (bereits vollständig nach site/assets/
kopiert) - dort erwartet diese Anleitung (und die
Convention-over-Configuration-Suche nach Autoren-Avataren unten)
lediglich Beitragsbilder/Autorenfotos, damit das eigene docs/assets/
eines Projekts nicht durch die Vermischung von Blog-Bildern mit dem Rest
seiner Diagramme und Icons unĂĽbersichtlich wird. Der Ort wird nirgends
erzwungen - jeder docs/assets/**-Pfad funktioniert in image/avatar.
Autoren
docs/blog/authors.yml ist optional - ein Eintrag pro Autoren-ID,
referenziert durch die eigene authors-Liste eines Beitrags:
lmajano:
name: Luis Majano
title: CEO, Ortus Solutions
bio: >
Founder of Ortus Solutions and creator of ColdBox, WireBox, and
BoxLang. Building developer tools since 2005.
url: https://github.com/lmajano
email: lmajano@ortussolutions.com
socials:
github: https://github.com/lmajano
twitter: https://x.com/lmajano
Nur name ist erforderlich. Jeder Autor, der von mindestens einem
Beitrag referenziert wird, erhält seine eigene
/blog/authors/<id>/-Seite (Bio, Socials, jeder Beitrag, den er
geschrieben hat) - ein Autor, dem noch niemand einen Beitrag zuschreibt,
bekommt keine Seite, selbst wenn er im Verzeichnis steht.
Avatar, per Konvention - lege eine Datei unter
docs/assets/blog/authors/<id>.{jpg,jpeg,png,webp,svg} ab, und sie wird
automatisch erkannt, kein avatar:-Schlüssel nötig. Ein explizites
avatar in authors.yml (eine URL oder ein zu docs/assets/-relativer
Pfad) ĂĽberschreibt immer die Convention-over-Configuration-Suche.
Kategorien, Archive, Pagination und der "Blog"-Nav-Eintrag
Jeder eigenständige categories-Wert über alle Beiträge hinweg erhält
seine eigene /blog/category/<slug>/-Seite, die nur die Beiträge dieser
einen Kategorie listet. Jedes Kalenderjahr mit mindestens einem Beitrag
erhält außerdem seine eigene /blog/archive/<year>/-Seite
(/blog/archive/2026/, /blog/archive/2025/, ...), vollständig aus der
eigenen date-Frontmatter jedes Beitrags abgeleitet - keine
Ordnerstruktur oder Dateinamenskonvention erforderlich, sodass es nie
darauf ankommt, wo die .md-Datei eines Beitrags unter
docs/blog/posts/ tatsächlich liegt (flach, oder in eigene Unterordner
zur einfacheren Durchsicht beim Bearbeiten aufgeteilt), ob das zu seinem
date passt. Die Hauptliste /blog/ erhält automatisch
"Nach Jahr durchsuchen"-/"Nach Kategorie durchsuchen"-Linkblöcke, jeweils
mit einer Beitragsanzahl pro Jahr/Kategorie, sobald Beiträge sich über
mehr als ein Jahr/eine Kategorie erstrecken - ein einzelnes Jahr oder
eine einzelne Kategorie allein lohnt keinen Linkblock, also wird er so
oder so weggelassen.
Die Hauptliste /blog/, jede Kategorie-Seite und jede
Jahresarchiv-Seite paginieren alle identisch - blog.postsPerPage in
der Website-Konfiguration steuert, wie viele Beiträge pro Seite
(Standard 10); ab Seite 2 geht es weiter zu .../page/2/,
.../page/3/ usw.
Ein einzelner "Blog"-Eintrag wird automatisch zur Hauptnavigation
hinzugefĂĽgt, sobald docs/blog/posts/ mindestens einen
Nicht-Entwurf-Beitrag hat - keine Änderung an nav/docs/nav.json
nötig. Standardmäßig wird er als Letztes angehängt, nach allem anderen.
Um ihn stattdessen an einer bestimmten Stelle zu platzieren, fĂĽge deinen
eigenen Eintrag mit einer expliziten url hinzu (umgeht die ĂĽbliche
Regel, dass path auf eine echte Seite passen muss, da der Blog keine
docs/-Seite ist) zu deinem nav-Array oder docs/nav.json - das
unterdrückt den automatisch angehängten Eintrag vollständig, es gibt
also nie ein Duplikat:
{ "nav": [
{ "path": "index.md" },
{ "title": "Blog", "url": "blog/index.html", "icon": "lucide:newspaper" },
{ "path": "about.md" }
] }
Einzelne Beiträge werden nicht selbst zur Navigation hinzugefügt
(genau wie der Tags-Index) - sie sind erreichbar von /blog/, ihrer
eigenen Kategorie-Seite, ihrer eigenen Jahresarchiv-Seite, der Seite
ihres Autors, der Suche und den Vorherige-/Nächste-Links
untereinander (Beiträge, die chronologisch benachbart sind, unabhängig
von der eigenen Vorherige-/Nächste-Kette der regulären Navigation).
Die eigene Meta-Zeile jedes Beitrags (auf seiner Card und seiner Detailseite) zeigt außerdem eine geschätzte Lesezeit neben dem Datum - eine grobe Schätzung Wortanzahl / 200 Wörter pro Minute, dieselbe Größenordnung, die die meisten Lesezeit-Funktionen verwenden, nicht konfigurierbar.
Feed
/blog/feed.xml - ein Standard-RSS-2.0-Feed der neuesten Beiträge,
neueste zuerst, geschrieben, sobald die Website-Konfiguration eine
absolute baseURL auflöst (dieselbe Voraussetzung wie bei
sitemap.xml) und blog.feed nicht auf false gesetzt ist. Jede
Kategorie erhält außerdem ihren eigenen gefilterten Feed unter
/blog/category/<slug>/feed.xml. Beide sind auf blog.feedLimit
Beiträge begrenzt (Standard 25) - die meisten Feed-Reader interessieren
sich nur fĂĽr das Neueste, sodass ein unbegrenzter Feed auf einem groĂźen
Blog bei jedem Poll einfach Bandbreite verschwendet; setze ihn auf 0
fĂĽr jeden Beitrag, unbegrenzt:
{ "blog": { "postsPerPage": 10, "feed": true, "feedLimit": 25 } }
EntwĂĽrfe ansehen
draft: true hält einen Beitrag vollständig aus einem echten
bxSites build heraus - aber bxSites serve bindet ihn trotzdem ein,
sodass du einen Entwurf durchlesen kannst (jeden Link anklicken, das
Beitragsbild prĂĽfen, sehen, wie er auf /blog/ gelistet wird), bevor er
fertig ist. Ein angezeigter Entwurf trägt immer ein sichtbares
"đźš§ Entwurf"-Banner - auf seiner eigenen Detailseite, und als Card mit
gestricheltem Rand ĂĽberall, wo er gelistet wird (die Hauptliste
/blog/, seine eigenen Kategorie-/Archiv-/Autoren-Seiten) - sodass es
nie eine Unklarheit darüber gibt, was tatsächlich veröffentlicht ist.
Stoppe bxSites serve und fĂĽhre bxSites build aus, und derselbe
Entwurf ist verschwunden, genau als hätte es ihn nie gegeben.
Statistiken
/blog/stats/ - eine Handvoll aggregierter Cards ĂĽber den Blog als
Ganzes: Beiträge insgesamt, Wörter insgesamt geschrieben,
durchschnittliche Lesezeit, Kategorie-/Mitwirkenden-/Jahres-Anzahlen,
und drei "Spotlight"-Cards (längster Beitrag, aktivste Kategorie,
aktivster Autor), jede verlinkt zur echten Seite, um die es geht. Rein
aus den für diesen Build bereits geladenen Beiträgen berechnet - keine
separate Analyse, kein Tracking, nichts zwischen Builds gespeichert -
und immer gebaut, selbst fĂĽr einen brandneuen Blog mit noch null
Beiträgen. Verlinkt vom unteren Ende der Hauptliste /blog/.
SEO und Social
Jeder Beitrag erhält bereits alles, was eine normale Seite auch bekommt
(<meta name="description">, og:description,
og:image+twitter:card, wenn ein Bild gesetzt ist - siehe
Konfiguration: ogImage), plus ein paar
beitragsspezifische Tags, die jedes integrierte Theme automatisch
hinzufĂĽgt: og:type ist "article" statt "website", und
article:published_time/article:author (einer pro anerkanntem Autor,
der ein url in authors.yml gesetzt hat) sind im <head> der Seite
enthalten.
Suche
Beiträge werden in dieselbe search-index.json indexiert wie jede
andere Seite (Modulspezifikation, Abschnitt 7) - keine separate
Blog-Such-UI, die bestehende Suchbox findet Beiträge bereits neben
Docs-Seiten.
Das Erscheinungsbild des Blogs anpassen
Es gibt kein separates "Blog-Theme" zu schreiben - jede Blog-Seite (die
Hauptliste /blog/, eine Kategorie-/Archiv-/Autoren-Seite,
/blog/stats/, und die eigene Detailseite jedes Beitrags) rendert ĂĽber
genau dasselbe layout.bxm/page.bxm wie jede andere Seite deiner
Website, sodass ein Blog automatisch wie der Rest deiner Docs aussieht,
und jede Theme-Ăśberschreibung, die du bereits vorgenommen hast (siehe
Themes), unverändert darauf angewendet
wird, ohne zusätzlichen Verdrahtungsaufwand.
Das blog-spezifische Markup selbst (Beitrags-Cards, die
Datum-/Autor-/Lesezeit-Meta-Zeile, der Pager, der Profilblock eines
Autors, die "Nach Jahr durchsuchen"-/"Nach Kategorie durchsuchen"-Link-Listen)
wird als schlichtes HTML mit einer Handvoll fester Klassennamen gebaut
und dann in page.contentHtml eingefĂĽgt, genau wie eine konvertierte
Markdown-Seite:
| Klasse | Wo sie auftaucht |
|---|---|
blog-post-card / blog-post-card--draft | Die Card jedes Beitrags auf /blog/, einer Kategorie-Seite oder einer Archiv-Seite |
blog-post-meta | Die Datum-/Autor-/Lesezeit-Zeile, auf einer Card und auf der eigenen Seite eines Beitrags |
blog-post-featured-image | Die image-Frontmatter eines Beitrags, auf seiner eigenen Detailseite |
blog-draft-badge | Das "đźš§ Entwurf"-Banner (nur bxSites serve) |
blog-pager | Vorherige-/Nächste-Pagination-Links auf einer paginierten Liste |
blog-author-profile | Der Bio-/Socials-Block eines Autors auf seiner /blog/authors/<id>/-Seite |
blog-archive-links / blog-category-links | Die "Nach Jahr durchsuchen"-/"Nach Kategorie durchsuchen"-Linkblöcke auf /blog/ |
Zwei Wege, es umzugestalten, genau wie bei jeder anderen Seite:
- Eine schnelle visuelle Anpassung - ziele mit deinem eigenen
extraCssauf diese Klassen, genauso wie du die Farben eines Themes anpasst. Die eigenen Regeln eines integrierten Themes für diese Klassen liegen in seinerassets/style.css(z. B.resources/themes/bootstrap/assets/style.css), wenn du einen Ausgangspunkt zum Überschreiben brauchst. - Strukturelle Änderungen - da Blog-Seiten
layout.bxm/page.bxmmit allem anderen teilen, ändert das Überschreiben eines Themes (oder das Schreiben eines von Grund auf) das Chrome des Blogs (Header, Nav, Footer, Artikel-Wrapper) gleich mit jeder anderen Seite - es gibt keine separate Blog-Vorlage zu kopieren.
Was du nicht tun kannst, ist, das Markup von Beitrags-Card/Pager/
Autoren-Profil selbst gegen ein eigenes auszutauschen - es wird einmal
von BlogBuilder.bx erzeugt, nicht aus einer Vorlagendatei in theme/
gelesen, sodass das Umgestalten mit CSS (oben) der unterstĂĽtzte Weg ist,
statt eine Pro-Komponenten-Ăśberschreibung.