Themes
Auf dieser Seite
- Integriert
- Ein veröffentlichtes Theme installieren
- Air-gapped/Offline-Websites
- Der ThemeProvider -Vertrag
- Mehrere Layouts pro Seite
- Welches Layout/Body gerade aktiv ist
- Farben anpassen, ohne ein Theme zu überschreiben
- Homepage-Hero-Banner
- Ein Theme überschreiben
- Projekt-Überschreibungen
- Slots
- Theme-Manifest ( theme.json )
- Implizite Layouts
- Ein Theme von Grund auf schreiben
Themes
Themes sind native BoxLang-.bxm-Templates - es gibt keine separate
Template-Engine oder einen eigenen Build-Schritt.
Integriert
| Theme | Basis | Hinweise |
|---|---|---|
bootstrap (Standard) | Bootstrap 5, vendoriert | Poppins-Schriftart, Navbar mit Marken-Gradient |
material | Handgefertigtes Material-artiges CSS | Card-Layout, Elevation-Schatten, Roboto-Schriftart |
tailwind | Tailwind Play CDN | Utility-Class-getrieben, kein Build-Schritt |
docsy | Handgefertigtes CSS, geforkt von material | An Read the Docs/Docsy angelehnter, marineblauer Referenzhandbuch-Look |
slate | Handgefertigtes CSS, geforkt von material | An Stripe/Slate angelehnt - eine dauerhaft dunkle Sidebar, unabhängig vom Hell-/Dunkelmodus |
docusaurus | Handgefertigtes CSS, geforkt von material | An Docusaurus angelehnte, kräftige, volle Breite farbige Navbar, abgerundete Cards |
justthedocs | Handgefertigtes CSS, geforkt von material | An Just the Docs angelehnter Minimalismus - die Suchbox sitzt oben in der Sidebar |
vuepress | Handgefertigtes CSS, geforkt von material | An VuePress angelehnter grüner Akzent, weich abgerundete Ecken |
gitbook | Handgefertigtes CSS, geforkt von material | An GitBook angelehnte zentrierte Lesespalte, Serifen-Überschriften |
notion | Handgefertigtes CSS, geforkt von material | An Notion angelehnte randlose Sidebar, nahezu grauskalige UI, großzügiger Weißraum |
Die sieben oben von material geforkten Themes verwenden materials
exakte BoxLang-Templates (layout.bxm/page.bxm/search.bxm) unverändert,
bis auf eine begrenzte CSS-Klassenpräfix-Umbenennung - nur
assets/style.css (und bei justthedocs eine verschobene
<bx:include>-Zeile, die die Suchbox in die Sidebar verlegt)
unterscheidet sich, sodass sie denselben vollständigen Funktionsumfang
und dasselbe air-gapped-fähige Verhalten erben, das material bereits
hat.
Das eigene CSS/JS jedes integrierten Themes (Bootstraps CSS/JS-Bundle,
highlight.js, Alpine.js, lunr.js für den standardmäßigen local-Suchanbieter,
und Mermaid, wenn mermaid aktiviert ist) wird vendoriert mit diesem Modul
ausgeliefert und direkt in jede gebaute site/ kopiert - kein CDN, kein
Internetzugang nötig, um eine gebaute Website anzusehen. Die eigene
Utility-Engine des tailwind-Themes (ein clientseitiger JIT-Compiler, kein
statisches Stylesheet) und andere optionale Funktionen, die du selbst
aktivierst (math, Algolia-Suche, Google Analytics), laden weiterhin von
einem CDN oder einer gehosteten API - siehe
Air-gapped/Offline-Websites unten.
bootstrap, material und tailwind verwenden dieselbe
BoxLang-Markenpalette (einen #00FF78 -> #00DBFF-Gradient und einen
#FFF500-Akzent); die sieben Galerie-Themes darunter verwenden stattdessen
jeweils ihre eigene, unterschiedliche Palette, angelehnt an die Plattform,
von der sie ihren Look entlehnen - siehe die Tabelle oben. Jedes der zehn
bringt unabhängig von der Palette denselben Satz an Seitenfunktionen mit:
- Ein Inhaltsverzeichnis "Auf dieser Seite", erzeugt aus den eigenen
h2/h3-Überschriften jeder Seite. - Breadcrumbs, die die Ahnenkette einer Seite zeigen, wenn sie mehr als eine Ebene tief unter einem verlinkten Vorfahren verschachtelt ist.
- Vorherige/Nächste-Seite-Links am Ende des Artikels, entlang der eigenen Leserichtung der Navigation.
- Syntax-hervorgehobene Codeblöcke, über highlight.js
plus eine eigene BoxLang-Grammatik (
```bx/```boxlang/```cfscript), jeweils mit einem Kopieren-Button - bei Hover sichtbar auf Geräten, die das unterstützen, auf Touch-Geräten immer sichtbar (dort gibt es kein Hover, um ihn einzublenden). Siehe Markdown-Erweiterungen. - Selbst gehostete Webfonts - keine Anfrage an
fonts.googleapis.comzur Anzeigezeit. - Ein Dunkel-/Hell-Modus-Umschalter, angetrieben von
Alpine.js für die Reaktivität. Die Wahl des
Besuchers wird in
localStoragegemerkt (mit Fallback auf die Betriebssystem-Präferenz) und noch vor dem ersten Rendern angewendet, um ein kurzes Aufblitzen des falschen Themes zu vermeiden. - Eine responsive Kopfzeile, die bei jeder Breite einreihig bleibt -
ein schmales Viewport schrumpft die Suchbox, statt sie in eine eigene
Zeile umbrechen zu lassen - plus eine einklappbare Sidebar-Navigation
(ein Hamburger-Umschalter in
bootstrap/material/tailwindgleichermaßen). - Tastaturkürzel in der Suchbox:
/fokussiert die Suche von überall auf der Seite, undEscapeschließt die Ergebnisse. Siehe Suche. - Ein Repo-Link und eine "Diese Seite bearbeiten"-/"Zuletzt
aktualisiert"-Zeile, wenn die Optionen
repo/lastUpdatedinbxsites.yamlgesetzt sind. Siehe Konfiguration. - Ein "Markdown herunterladen"-Link, neben "Diese Seite bearbeiten" -
die rohe
.md-Quelle jeder Seite wird zusammen mit ihrem gebauten HTML veröffentlicht (guides/themes.mdliegt nebenguides/themes/index.html), sodass sie (oder eine KI) die Seite direkt als reines Markdown lesen kann, statt gerendertes HTML zu parsen. Immer aktiv, keine Konfiguration nötig. Siehe Erste Schritte. - Eine optionale Fußzeile (Copyright,
social-Links, ein "Built with BxSites"-Hinweis), wennfooterinbxsites.yamltrueist. Siehe Konfiguration. - Ein Versionsumschalter, der automatisch erscheint, sobald ein
Projekt einen
docs/versions/-Ordner mit mehr als einer Version hat. Siehe Konfiguration. - Eine themenspezifische
404.html, automatisch ausgeliefert von den meisten statischen Hosts (einschließlich GitHub Pages) für jeden nicht gefundenen Pfad. Lege eine404.mdim Stammverzeichnis vondocs/(odersrc/) an, um Titel und Inhalt selbst zu bestimmen - sie wird nie als reguläre Seite gebaut (kein Nav-Eintrag, keinesitemap.xml-URL), sondern stattdessen direkt insite/404.htmlgerendert. - Ein eigenes Logo und Favicon, wenn
theme.logo/theme.faviconinbxsites.yamlgesetzt sind. Siehe Konfiguration. - Eine einklappbare Sidebar-Navigation, opt-in über
theme.options.navCollapsible- jeder Abschnitt mit Kindern (verlinkt oder nicht) erhält eine Umschalt-Schaltfläche, statt seine Kinder immer inline anzuzeigen, und der Abschnitt, der die aktuelle Seite enthält, startet immer geöffnet. Siehe Konfiguration. - Google Analytics, wenn
analyticsinbxsites.yamlkonfiguriert ist. Siehe Konfiguration. - Social-Share-Cards (Open-Graph- + Twitter-Card-Meta-Tags), erzeugt
aus der
description-Frontmatter jeder Seite (oder der websiteweitendescription) und ihrem eigenenogImage(oder dem websiteweiten) - optional automatisch pro Seite erzeugt übergenerateOgImagesinbxsites.yaml. Siehe Konfiguration. - Seiten-Tags, ein Icon und eine Zusammenfassungszeile, alle opt-in
über die eigene Frontmatter einer Seite - Tags werden als Badges
gerendert, die zu einem websiteweiten
/tags/-Index verlinken. Siehe Erste Schritte. - Eine explizite Navigations-Überschreibung, in
bxsites.yamloder der eigenendocs/nav.json, die die Ordner-Ableitung für große Websites ersetzt. Siehe Konfiguration. - Zusätzliches CSS/JS, eingebunden über
extraCss/extraJsinbxsites.yaml. Siehe Konfiguration. - Admonition-Callout-Boxen (Hinweis/Warnung/Tipp/...), standardmäßig im Markdown jeder Seite aktiv, einschließlich einklappbarer Varianten - keine Konfiguration nötig. Siehe Markdown-Erweiterungen.
- Fußnoten und Definitionslisten, opt-in über
markdowninbxsites.yaml. Siehe Markdown-Erweiterungen. - Content-Tabs, Code-Zeilennummern/hervorgehobene Zeilen/Titel und Diff-Markierungen/Terminal-Rahmen für Codeblöcke, keine Konfiguration nötig. Siehe Markdown-Erweiterungen.
- Mermaid-Diagramme, opt-in über
mermaidinbxsites.yaml. Siehe Markdown-Erweiterungen. - Mathematik (KaTeX), opt-in über
mathinbxsites.yaml. Siehe Markdown-Erweiterungen.
Lege in bxsites.yaml fest, welches Theme ein Projekt verwendet:
theme: { name: material }
{ "theme": { "name": "material" } }
[theme]
name = "material"
Ein veröffentlichtes Theme installieren
Ein auf ForgeBox veröffentlichtes Theme installiert sich mit nichts
weiter als der bxSites-Binary selbst - kein box/CommandBox nötig.
Durchsuche die bereits veröffentlichten Pakete unter der Kategorie
bxsites-themes auf
ForgeBox:
bxSites install:theme --name=bx-sites-theme-blog1 [--version=1.0.0]
Das lädt das ZIP des Pakets herunter und entpackt es nach
.themes/bx-sites-theme-blog1/ innerhalb des Content-Root des Projekts
(siehe Content-Quelle),
wobei vor Abschluss geprüft wird, ob es den ThemeProvider-Vertrag unten
erfüllt. Ein Projekt kann auf diese Weise mehrere installierte Themes
nebeneinander vorhalten und rein per Name zwischen ihnen wechseln:
theme: { name: bx-sites-theme-blog1 }
{ "theme": { "name": "bx-sites-theme-blog1" } }
[theme]
name = "bx-sites-theme-blog1"
Ein Theme braucht überhaupt keine BoxLang-Modul-/Klassenlader-Beteiligung
(anders als ein Plugin) - es sind reine Dateien, es gibt also keinen
separaten Aktivierungsschritt, wie ihn install:plugin hat; das Setzen
von theme.name ist die einzige nötige Verdrahtung. Siehe
install:theme in der CLI-Referenz.
Startest du stattdessen von einem Theme, das für einen anderen
statischen Site-Generator gebaut wurde? Siehe
Ein Theme importieren - theme:import wandelt die
eigenen Template-Dateien eines mkdocs-/jekyll-/hugo-Themes mechanisch in
ein bestmögliches .themes/<name>/-Gerüst um.
Air-gapped/Offline-Websites
Eine gebaute Website funktioniert standardmäßig völlig ohne Internetzugang,
für bootstrap, material und die sieben von material geforkten
Themes (docsy, slate, docusaurus, justthedocs, vuepress,
gitbook, notion) mit dem standardmäßigen local-Suchanbieter:
Bootstraps eigenes CSS/JS, highlight.js, Alpine.js und
lunr.js sind alle mit diesem Modul vendoriert (resources/assets/vendor/)
und werden zur Build-Zeit direkt nach site/assets/vendor/ kopiert -
nirgends im erzeugten HTML taucht dafür ein CDN-<script>-/<link>-Tag
auf. Aktivierst du den Schlüssel mermaid in bxsites.yaml, wird Mermaid
auf dieselbe Weise vendoriert - sein mermaid.min.js-Bundle wird nach
site/assets/vendor/mermaid/ kopiert, und jedes integrierte Theme lädt es
von dort, sodass Diagramme weiterhin ganz ohne ausgehende Anfragen
gerendert werden.
Ein paar Dinge greifen weiterhin nach außen ins Netzwerk, aber nur, wenn du sie selbst aktivierst:
- Die eigene Utility-Engine des
tailwind-Themes ist ein clientseitiger JIT-Compiler, geladen voncdn.tailwindcss.com- kein statisches Stylesheet, das dieses Modul auf dieselbe Weise vendorieren könnte, daher ist dieses Theme (noch) nicht air-gapped-fähig. - Mermaids eigene Layout-Engine lädt bei Bedarf einen zusätzlichen Chunk
nach,
elk-api.js, von jsDelivr - aber nur für Diagrammtypen, die denelk-Layout-Algorithmus verwenden; das vendoriertemermaid.min.jsrendert jeden anderen Diagrammtyp vollständig eigenständig. - Die Option
mathinbxsites.yamllädt KaTeX (sowohl dessen JS als auch die eigenen Font-Dateien) von einem CDN, wenn sie aktiviert ist. searchProvider.provider: "algolia"undanalytics.provider: "google"sprechen naturgemäß mit einer gehosteten API/einem Tracking-Endpunkt - das Vendorieren der JS-Datei würde diese Abhängigkeit nicht beseitigen.
Wenn dein Einsatzziel wirklich null Internetzugang hat, bleib bei
bootstrap/material/einem der sieben von material geforkten Themes,
dem standardmäßigen local-Suchanbieter, vermeide
elk-Layout-Mermaid-Diagramme, falls mermaid aktiviert ist, und lass
math/Algolia/Analytics aus.
Siehe Icons dafür, wie die eigene icon-Frontmatter einer
Seite (oder das eigene icon eines nav.json-Eintrags) zu einem Emoji,
einem benannten Icon aus einer von acht mitgelieferten Bibliotheken oder
einer eigenen, projekteigenen SVG-Datei aufgelöst wird.
Der ThemeProvider-Vertrag
Ein Theme ist einfach ein Ordner mit:
layout.bxm(erforderlich) - die äußere HTML-Hülle + Navigation. Erhältvariables.page,variables.nav,variables.siteConfig,variables.themeDirundvariables.basePathim Scope, und bindet das aufgelöste Body-Template über#variables.themeDir#/#variables.bodyFile#ein (page.bxm, sofern eine Seite/ein Inhaltstyp nicht auf etwas anderes auflöst - siehe Mehrere Layouts pro Seite unten).variables.basePathist immer ein root-relativer, auf/endender Pfad (standardmäßig/,/my-docs/, wennbaseURLinbxsites.yamldas überschreibt) - stelle diesen jedem internenhref/srcvoran, statt ein führendes/fest zu codieren, damit das Theme auch funktioniert, wenn die Website aus einem Unterpfad ausgeliefert wird.page.bxm(erforderlich) - der Artikelinhalt. Rendertvariables.page.contentHtml- das bereits konvertierte Markdown.search.bxm(optional) - das Markup der Suchbox, vonlayout.bxmnur eingebunden, wennsearchinbxsites.yamltrueist. Siehe Suche.blog.bxm(optional) - eine alternative äußere Hülle für die eigenen Listen-/Kategorie-/Archiv-/Autoren-/Statistikseiten des Blogs. Fällt auflayout.bxmzurück, wenn ein Theme sie nicht hat. Siehe Mehrere Layouts pro Seite.blog-page.bxm(optional) - ein alternativer Body für einen einzelnen Blogbeitrag, weiterhin gerendert unter dem normalenlayout.bxmdes Themes. Fällt aufpage.bxmzurück, wenn ein Theme sie nicht hat.assets/(optional) - Theme-CSS/JS, zur Build-Zeit nachsite/assets/theme/kopiert.slots/<name>.bxm(optional) - der Standardinhalt des Themes für einen Slot, eingebunden überall dort, wo seinlayout.bxmrenderSlot( "<name>" )aufruft.theme.json(optional) - das Manifest des Themes: sein Slug und die Slots, die es bereitstellt.
variables.page.editUrl/.lastUpdated (leere Zeichenketten, wenn nicht
konfiguriert) und variables.siteConfig.repo/.social/.footer sind
ebenfalls immer verfügbar und stützen die oben beschriebenen
Repo-Link-/Bearbeiten-Link-/Zuletzt-aktualisiert-/Fußzeilen-Funktionen -
ein eigenes Theme entscheidet selbst, ob und wie es sie rendert, wie auch
alles andere. variables.versions ([ { label, url, kind } ], "Latest"
zuerst) und variables.currentVersion (das gerade gerenderte label)
stützen den Versionsumschalter - leer/"Latest" für ein nicht
versioniertes Projekt, sodass ein Theme nur dann einen Umschalter rendern
muss, wenn variables.versions.len() gt 1. Das kind jedes Eintrags ist
"current" (die Version, auf der ein Besucher an der Website-Wurzel
landet), "released" (jeder andere angelegte Snapshot) oder "next"
(der reine docs/-Baum, sobald versions.default gesetzt ist - siehe
Versionierung).
Statt variables.versions von Hand zu durchlaufen, ruf
renderVersionSwitcher( variables.versions, variables.currentVersion )
auf - eingebunden in den Render-Scope jedes Themes auf dieselbe Weise wie
themeAssetUrl()/moduleAssetUrl() (oben) - und es baut das gesamte
<select> auf, gruppiert "next" hinter einer Trennlinie nach jeder
veröffentlichten Version, und beschriftet den "current"-Eintrag mit
"(Current)", alles gesteuert über kind, statt dass ein Theme diese
Logik selbst nachbaut. Ein optionales zweites Argument fügt dem <select>
zusätzliche CSS-Klassen hinzu (renderVersionSwitcher( variables.versions, variables.currentVersion, "my-extra-class" )) für ein Theme, das sein
eigenes Styling möchte, ohne das Markup neu zu implementieren. Jedes
integrierte Theme bezieht seine Repo-/Social-Icons aus einem kleinen
gemeinsamen SVG-Lookup,
<bx:include template="#variables.moduleAssetsDir#/icons.bxm">
(definiert bxsitesIcon( name ), eines von github, twitter/x, rss,
youtube, linkedin, facebook, bluesky, threads, slack,
patreon, email, edit, clock, mit einem generischen Link-Symbol als
Fallback) - ein eigenes Theme kann das auf dieselbe Weise einbinden, oder
seine Icons vollständig selbst bereitstellen.
Ein Theme-Ordner, dem eine der beiden erforderlichen Dateien fehlt,
schlägt sofort mit einem klaren BxSites.InvalidTheme-Fehler zur Build-Zeit
fehl, statt mit einem verwirrenden Template-Fehler tief im Rendering.
blog.bxm/blog-page.bxm werden nie erzwungen - dieselbe "optional, fällt
zurück"-Form, die search.bxm bereits hat.
Mehrere Layouts pro Seite
Ein Theme kann mehr als eine äußere Hülle und mehr als ein Body-Template anbieten, sodass ein Blog (oder ein beliebiger anderer Inhaltstyp) anders aussehen kann als der Rest der Website, während dieselbe Chrome und Assets des Themes weiterverwendet werden. Zwei unabhängige Auflösungsketten:
- Äußere Hülle - standardmäßig
layout.bxm. Die eigenen Listen-/Kategorie-/Archiv-/Autoren-/Statistikseiten des Blogs verwendenblog.bxm, wenn das aktive Theme es hat. Eine Seite, dielayout:in ihrem Frontmatter setzt (oder auf eine Regel für implizite Layouts passt), wird stattdessen über<name>.bxmgerendert, sofern das aktive Theme oder die Überschreibungen des Projekts eine haben. - Body - standardmäßig
page.bxm. Ein Blogbeitrag verwendetblog-page.bxm, wenn das aktive Theme es hat. Eine Hülle bindet den Body mitinclude "#variables.themeDir#/#variables.bodyFile#"ein.
---
title: We raised a Series A
layout: press-release
---
layout: press-release rendert diese eine Seite über
.overrides/<theme>/press-release.bxm (oder das eigene press-release.bxm
des aktiven Themes, falls vorhanden) anstelle von layout.bxm. Diese Datei
ist eine vollständige Seite: Kopiere layout.bxm, um mit der eigenen Chrome
des Themes zu starten (bxSites layout:new press-release tut genau das),
oder schreibe eine minimale und binde den Body per include dort ein, wo er
hingehört. Ein layout:, das eine Datei benennt, die niemand bereitstellt,
fällt auf layout.bxm/page.bxm zurück, statt den Build fehlschlagen zu
lassen, sodass ein Themewechsel niemals eine Seite bricht, die das eigene
benutzerdefinierte Layout eines anderen Themes benannt hat.
bootstrap liefert blog.bxm/blog-page.bxm als funktionierendes
Beispiel zum Kopieren mit; die anderen integrierten Themes (noch) nicht
und fallen für Blog-Inhalte auf layout.bxm/page.bxm zurück, genau wie
es ein unvollständiges .theme/-Override auch täte.
Welches Layout/Body gerade aktiv ist
Jede .bxm-Datei, durch die eine Seite gerendert wird - layout.bxm,
page.bxm, blog.bxm, blog-page.bxm, oder eine eigene benutzerdefinierte
eines Projekts - kann auslesen, welche Dateien tatsächlich aufgelöst
wurden, auf dieselbe direkte Art, wie sie bereits variables.page/
variables.data liest:
variables.layoutFile- die aktive äußere Hülle, z. B."layout.bxm"oder"blog.bxm"variables.bodyFile- der aktive Body, z. B."page.bxm","blog-page.bxm", oder ein per Frontmatter benanntervariables.page.layout- der eigene, unverarbeitete Frontmatter-Wertlayout:der Seite, falls einer gesetzt wurde; sonst""
Nützlich als Hook für eine Body-Klasse, oder um ohne separates Template zu verzweigen:
<body class="layout-#reReplace( variables.bodyFile, '\.bxm$', '' )#">
<bx:if variables.bodyFile == "blog-page.bxm">
<!-- Chrome nur für Blogbeiträge -->
</bx:if>
Farben anpassen, ohne ein Theme zu überschreiben
Für eine kleine Farb-/Schriftanpassung ist ein ganzes Theme zu forken
Overkill - jedes integrierte Theme liest seine Palette aus einer Handvoll
CSS-Custom-Properties auf :root, erneut deklariert unter
[data-theme="dark"] für den Dunkelmodus. Das
extraCss von bxsites.yaml wird
nach dem eigenen Stylesheet des Themes geladen, sodass eine erneute
Deklaration mit gleicher Spezifität darin gewinnt, ohne resources/themes/
überhaupt anzurühren:
extraCss: [ assets/brand.css ]
{ "extraCss": ["assets/brand.css"] }
extraCss = [ "assets/brand.css" ]
/* docs/assets/brand.css - copied to site/assets/brand.css at build time */
:root {
--bxsites-gradient-start: #7C3AED;
--bxsites-gradient-end: #DB2777;
--bxsites-accent: #FBBF24;
--bxsites-link: #7C3AED;
--bxsites-link-hover: #9F5AF0;
}
[data-theme="dark"] {
--bxsites-link: #C4B5FD;
--bxsites-link-hover: #DDD6FE;
}
Das eigene Set des bootstrap-Themes
(resources/themes/bootstrap/assets/style.css) ist
--bxsites-gradient-start/-end, --bxsites-accent, --bxsites-bg,
--bxsites-text, --bxsites-sidebar-bg, --bxsites-sidebar-text,
--bxsites-border, --bxsites-link, --bxsites-link-hover,
--bxsites-code-bg, --bxsites-step-marker-bg, --bxsites-step-marker-text,
--bxsites-step-line, --bxsites-step-success-bg/-text,
--bxsites-step-warning-bg/-text, --bxsites-step-danger-bg/-text und
das --bxsites-table-*-Set, das bei einer gerenderten
Tabelle Karte, Kopfleiste, Trennlinien und Zebra-/Hover-Tönung zeichnet.
Jedes integrierte Theme garantiert --bxsites-gradient-start/-end,
--bxsites-accent sowie die Sets --bxsites-step-* und
--bxsites-table-* unter genau diesen Namen, sodass extraCss unabhängig vom Theme immer die
Markenfarbe/Stepper-Akzente umlenken kann - aber nur bootstrap, slate
und notion legen zusätzlich auch
--bxsites-bg/-text/-sidebar-bg/-sidebar-text/-border/-link/-link-hover/-code-bg
unter diesen Namen offen (justthedocs aliast alle bis auf die beiden
-sidebar-* auf dieselbe Weise). Jedes andere integrierte Theme
(material, tailwind, docsy, docusaurus, vuepress, gitbook)
verwendet für diese zweite Gruppe stattdessen seine eigenen internen
Custom-Property-Namen (z. B. verwendet materials eigenes
assets/style.css --md-bg/--md-ink/--md-link/...) - öffne das
eigene assets/style.css dieses Themes, um seine echten Namen zu finden,
bevor du eines davon über extraCss überschreibst. Alles über
Farbe/Schriftart hinaus (Layout, Chrome hinzufügen/entfernen) braucht
eine echte Überschreibung oder ein eigenes Theme - siehe unten.
Der Rest stützt den ::: stepper/::: step-Direktiv-Block
--bxsites-step-marker-bg/-textsind die Hintergrund-/Textfarbe des standardmäßigen nummerierten Kreises (bootstrap/materialsetzen ihn standardmäßig auf den eigenen--bxsites-accentdes Themes;tailwindverwendet ein eigenes Türkis-/Minz-Paar, da es keinen einzelnen gemeinsamen Akzent-Token hat),--bxsites-step-lineist die Verbindungslinie zwischen den Schritten, und die-success/-warning/-danger-Paare stützen das eigene, optionalecolor="..."-Attribut eines Schritts - anders als der Standard-Marker sind diese drei dasselbe feste Hintergrund-/Text-Paar sowohl im Hell- als auch im Dunkelmodus (ein in sich geschlossenes Badge, nicht an die eigene Markenfarbe des Themes gebunden), es gibt also keine[data-theme="dark"]-Überschreibung neu zu deklarieren:
:root {
--bxsites-step-marker-bg: #7C3AED;
--bxsites-step-marker-text: #fff;
--bxsites-step-success-bg: #059669;
--bxsites-step-success-text: #fff;
}
[data-theme="dark"] {
--bxsites-step-marker-bg: #C4B5FD;
--bxsites-step-marker-text: #1b1f21;
}
Homepage-Hero-Banner
Jedes integrierte Theme liefert CSS für ein Homepage-Banner in voller
Breite mit Titelbild und Call-to-Action-Buttons - genau diese Website
verwendet es in ihrer eigenen docs/index.md. Es gibt dafür keinen
Direktiv-Block und keine Konfiguration, nur schlichtes HTML, das jede
Seite einfügen kann (eine Homepage ist einfach eine normale Seite,
order: 1 oder sonst als Erste in der Navigation):
<div class="bxsites-hero">
<img class="bxsites-hero__banner" src="assets/home-banner.jpg" alt="...">
<div class="bxsites-hero__actions">
<a class="bxsites-hero__btn bxsites-hero__btn--primary" href="getting-started.md">Get Started</a>
<a class="bxsites-hero__btn bxsites-hero__btn--secondary" href="https://github.com/your/repo">View on GitHub</a>
</div>
</div>
bxsites-hero__btn--primary/--secondary sind dieselben zwei
Akzent-Stile, die jedes Theme bereits an anderer Stelle verwendet -
tausche, entferne oder füge Buttons nach Belieben hinzu, und
skaliere/ersetze das eigene Bild von bxsites-hero__banner über ein zu
docs/assets/-relatives src, auf dieselbe Weise, wie jedes andere Bild
aufgelöst wird.
Ein Theme überschreiben
Lege deine eigenen layout.bxm + page.bxm (und optional search.bxm /
assets/) in einen .theme/-Ordner innerhalb des Content-Root deines
Projekts - docs/.theme/, src/.theme/, oder <source>/.theme/ für
das, worauf source auflöst (siehe Content-Quelle),
nicht im bloßen Projekt-Wurzelverzeichnis. BxSites bevorzugt eine
.theme/-Überschreibung im Content-Root sowohl gegenüber einem
installierten .themes/<name>/-Theme als auch gegenüber jedem
integrierten Theme, solange sie den obigen Vertrag erfüllt - die
integrierten Themes unter resources/themes/ dieses Moduls sind ein
guter Ausgangspunkt zum Kopieren und Anpassen. Vollständige
Auflösungsreihenfolge: .theme/ (dieser Abschnitt) ->
.themes/theme.name/ (ein
installiertes Theme, falls
theme.name zu einem passt) -> ein integriertes Theme namens
theme.name.
Ein durchgearbeitetes Beispiel - starte mit bootstrap und tausche seine
Markenpalette und Überschriften-Schriftart gegen deine eigenen aus, wobei
alles andere (Navigation, Suche, Dunkelmodus, Code-Hervorhebung, ...)
genau so weiterläuft, wie es bereits funktioniert:
my-project/
├── bxsites.yaml
└── docs/
├── index.md
└── .theme/ ← content-root override, checked before any built-in theme
├── layout.bxm ← copied from resources/themes/bootstrap/layout.bxm
├── page.bxm ← copied from resources/themes/bootstrap/page.bxm, unchanged
├── search.bxm ← copied unchanged
└── assets/
└── style.css ← copied from bootstrap's assets/style.css, then edited
- Kopiere die drei
.bxm-Dateien undassets/style.cssausresources/themes/bootstrap/dieses Moduls in dasdocs/.theme/deines Projekts (odersrc/.theme/, oder<source>/.theme/, je nachdem, wo dein Content-Root tatsächlich liegt). - Ändere nur das, was du wirklich ändern musst. Um Markenpalette und
Schriftart zu tauschen, genügt der obere Teil von
.theme/assets/style.css:
:root {
--bxsites-gradient-start: #7C3AED; /* was #00FF78 */
--bxsites-gradient-end: #DB2777; /* was #00DBFF */
--bxsites-accent: #FBBF24; /* was #FFF500 */
}
body {
font-family: "Inter", system-ui, sans-serif; /* was "Poppins" */
}
- Führe
bxSites buildaus (oderservewährend der Iteration) - BxSites übernimmt.theme/automatisch, keine Änderung anbxsites.yamlnötig (ein.theme/-Ordner im Content-Root hat immer Vorrang vor dem imtheme.namegenannten integrierten Theme). Alles, was du nicht angefasst hast - Navigations-Rendering, Suche, der Dunkelmodus-Umschalter, Code-Annotationen - funktioniert genau so weiter, wie es im ursprünglichenbootstrap-Theme funktioniert hat, da es darunter immer noch genau dasselbelayout.bxm/page.bxm-Markup ist.
Ein .theme/-Ordner eines Projekts ist allerdings alles-oder-nichts -
sobald BxSites einen findet, wird er anstelle des integrierten Themes
vollständig verwendet, braucht also trotzdem seine eigenen layout.bxm +
page.bxm, selbst wenn du nur assets/style.css geändert hast (ein
Ordner, dem eine der beiden fehlt, schlägt sofort mit
BxSites.InvalidTheme fehl, statt stillschweigend zurückzufallen). Für
eine reine CSS-Anpassung ohne .bxm, nutze stattdessen
extraCss von oben - es
legt sich über das Theme, das bxsites.yaml benennt, ganz ohne
.theme/-Ordner. .theme/ ist für den Fall, dass du auch das Markup
selbst ändern musst, siehe als Nächstes.
Projekt-Überschreibungen
Ein .theme/-Ordner ersetzt ein Theme vollständig. Um nur ein paar Dinge zu
ändern und alles andere zu behalten, einschließlich künftiger Theme-Updates,
lege die Änderungen stattdessen in .overrides/<theme>/ innerhalb des
Content-Root ab. <theme> ist der Slug des Themes, der Wert von
theme.name:
docs/
└── .overrides/
└── bootstrap/ ← applies only while theme.name is bootstrap
├── home.bxm ← a layout, used by `layout: home`
├── page.bxm ← replaces the theme's page.bxm
├── assets/
│ └── logo.svg ← replaces (or adds) one theme asset
└── slots/
└── footer.bxm ← fills the theme's `footer` slot
Jede Datei in .overrides/<theme>/ wird über das Theme gelegt: Eine Datei
mit demselben Namen ersetzt die des Themes, alles andere wird hinzugefügt.
Das Theme selbst wird nie verändert. Zur Build-Zeit wird das Ergebnis in
.cache/theme/<theme>/ zusammengeführt (von bxSites clean entfernt), und
dieser zusammengeführte Ordner wird gerendert, sodass themeAssetUrl(),
Includes und Fingerprinting genauso funktionieren wie bei einem normalen
Theme.
- Ein Themewechsel wechselt die Überschreibungen mit. Ein
material-Build ignoriert.overrides/bootstrap/; gib ihm sein eigenes.overrides/material/. - Ein Theme-Name muss ein Slug sein: Kleinbuchstaben, Ziffern und
Bindestriche, der Wert, den BoxLangs
slugify()dafür zurückgibt (bxSitesschlägt andernfalls mit einem Vorschlag fehl). Er benennt den Ordner des Themes, seine.themes/<slug>/-Installation und seinen.overrides/<slug>/-Ordner. - Auflösungsreihenfolge für jede Datei: ein vollständiges flaches
.theme/(hat das Projekt eines, gewinnt es auf ganzer Linie), dann.overrides/<theme>/, dann ein installiertes.themes/<theme>/, dann das integrierte Theme. .overrides/ist ein Punkt-Ordner und wird daher nie als Seiten veröffentlicht.
bxSites layout:new <name> legt ein Layout in diesem Ordner an.
Slots
Ein Slot ist ein benanntes Loch in der layout.bxm eines Themes, das ein
Projekt füllen kann, ohne das Layout zu kopieren. Lege slots/<name>.bxm im
Überschreibungsordner an:
<bx:output>
<div class="alert alert-info text-center mb-0">
v1.0 is out. <a href="#variables.basePath#releases/1.0.md">Read the notes</a>
</div>
</bx:output>
Jedes integrierte Theme stellt dieselben fünf Slots bereit:
| Slot | Wo er gerendert wird | Theme-Standard |
|---|---|---|
head-extra | Direkt vor </head>: zusätzliche <meta>, <link>, Skripte | leer |
announcement-bar | Direkt nach <body>, oberhalb der Kopfzeile | leer |
header | Die obere Navigationsleiste | die eigene Kopfzeile des Themes |
footer | Die Fußzeile der Seite | die eigene Fußzeile des Themes (bei footer: true) |
sidebar-bottom | Unterhalb des Navigationsbaums in der Seitenleiste | leer |
- Eine Überschreibung ersetzt den Standard. Bei
headerundfooterbedeutet das, das Markup des Themes zu ersetzen; bei den anderen füllt sie einen leeren Slot. Lösche die Datei, um den Standard zurückzubekommen. - Eine Slot-Datei ist ein Template. Sie sieht dieselben Variablen wie ein
Layout (
page,nav,siteConfig,basePath,data, ...) und umschließt ihr Markup, wielayout.bxm, mit<bx:output>, damit#expressions#gerendert werden. - Ein Theme kann mehr Slots bereitstellen als die fünf; sein Manifest listet sie auf.
- Ein Slot-Name, den das Theme nicht bereitstellt, gibt eine Build-Warnung aus und wird ignoriert. Er lässt den Build nie fehlschlagen, sodass ein Themewechsel unbedenklich ist.
Einen Slot aus deinem eigenen Layout rendern:
#renderSlot( "footer" )#
Theme-Manifest (theme.json)
Eine optionale Datei im Wurzelverzeichnis eines Theme-Ordners. Alle 10 integrierten Themes liefern eine mit.
{
"slug": "bootstrap",
"name": "Bootstrap",
"slots": ["head-extra", "announcement-bar", "header", "footer", "sidebar-bottom"]
}
slugmuss dem Ordnernamen des Themes entsprechen; eine Abweichung lässt den Build mitBxSites.InvalidThemefehlschlagen.slotslistet die Slot-Namen auf, die das Layout des Themes rendert, jeweils Kleinbuchstaben, Ziffern und Bindestriche. Mit einem Manifest geben sowohl einrenderSlot()-Aufruf für einen nicht aufgeführten Namen als auch eine Überschreibungs-Slot-Datei, die das Theme nicht bereitstellt, eine Warnung aus.- Ohne Manifest funktioniert ein Theme wie bisher; Slot-Namen werden nicht geprüft.
Implizite Layouts
Statt layout: landing in das Frontmatter jeder Seite zu wiederholen, weise
einer Gruppe von Seiten in bxsites.yaml per Pfad ein Layout zu. Das
funktioniert wie die impliziten Layouts von ColdBox.
layouts:
- layout: landing
match: ["marketing/**", "pricing.md"]
- layout: post
match: "blog/posts/**"
layoutist ein bloßer Template-Name, aufgelöst wie ein Frontmatter-layout:(.overrides/<theme>/<name>.bxm, dann das des Themes, dann der Standard).matchist ein Glob oder eine Liste davon. Die Regeln werden der Reihe nach geprüft und der erste Treffer gewinnt.- Das eigene
layout:-Frontmatter einer Seite gewinnt immer gegenüber einer Regel. - Globs werden gegen den Pfad der Seite relativ zum Content-Root abgeglichen,
mit Schrägstrichen und unter Beachtung der Groß-/Kleinschreibung:
*ist eine beliebige Zeichenfolge innerhalb eines Segments,**überschreitet Segmente (**/passt auch auf null Ordner),?ist ein einzelnes Zeichen, und ein abschließendes/bedeutet alles unterhalb dieses Ordners.marketing/**passt aufmarketing/a.mdundmarketing/deep/b.md, nicht aufmarketing.md. - Regeln gelten für Doku-Seiten und Blogbeiträge (der Pfad eines Beitrags ist
blog/posts/<file>). Generierte Seiten (Blog-Listen, Tags, Suche, 404) behalten ihre eigenen Templates. - Dieselben Regeln gelten in versionierten und übersetzten Bäumen, abgeglichen gegen den Pfad innerhalb dieses Baums.
Ein Theme von Grund auf schreiben
Ein Theme braucht nur die beiden erforderlichen Dateien, hier also ein
wirklich minimales Beispiel - kein Bootstrap/Tailwind, kein Dunkelmodus,
keine Such-UI - um genau zu zeigen, was erforderlich ist im Vergleich zu
dem, was die integrierten Themes zusätzlich bieten. Speichere beide als
docs/.theme/layout.bxm und docs/.theme/page.bxm in deinem Projekt
(oder unter dem Content-Root, auf den dein source auflöst) - ein
.theme/-Ordner im Content-Root wird automatisch übernommen (wie oben),
keine Änderung an bxsites.yaml nötig:
<!-- docs/.theme/layout.bxm -->
<bx:script>
function renderNav( required array nodes ) {
var html = "<ul>"
for ( var node in arguments.nodes ) {
html &= "<li>"
html &= len( node.url )
? '<a href="' & variables.basePath & node.url & '">' & encodeForHTML( node.title ) & '</a>'
: encodeForHTML( node.title )
if ( node.children.len() ) {
html &= renderNav( node.children )
}
html &= "</li>"
}
return html & "</ul>"
}
</bx:script>
<bx:output>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>#encodeForHTML( variables.page.title )# - #encodeForHTML( variables.siteConfig.name )#</title>
<link rel="stylesheet" href="#themeAssetUrl( 'style.css' )#">
</head>
<body>
<header><a href="#variables.basePath#">#encodeForHTML( variables.siteConfig.name )#</a></header>
<nav>#renderNav( variables.nav )#</nav>
<main>
</bx:output>
<bx:include template="#variables.themeDir#/#variables.bodyFile#">
<bx:output>
</main>
</body>
</html>
</bx:output>
<!-- docs/.theme/page.bxm -->
<bx:output>
<article>
<h1>#encodeForHTML( variables.page.title )#</h1>
#variables.page.contentHtml#
</article>
</bx:output>
Das ist ein vollständiges, funktionierendes Theme -
variables.page.contentHtml ist das bereits konvertierte Markdown
(Syntax-Hervorhebung, Admonitions, Tabs, Mathematik und alles), es bleibt
also nichts mehr zu parsen, nur noch zu layouten. Von hier aus füge
hinzu, was die integrierten Themes haben und du tatsächlich willst:
search.bxm (nur eingebunden, wenn search in bxsites.yaml true ist
- siehe Suche), einen Dunkelmodus-Umschalter (kopiere das
x-data/x-init-Alpine.js-Paar vom<body>-Tag vonresources/themes/bootstrap/layout.bxmund den passenden[data-theme="dark"]-CSS-Block), Breadcrumbs/Tags/Vorherige-Nächste-Links (page.bxmin jedem integrierten Theme zeigt das Muster - jedes ist nur einifum eine kleine Render-Funktion, alle gespeist aus Feldern, die bereits aufvariables.pagevorhanden sind), oder einenassets/-Ordner für dein eigenes CSS/JS, automatisch zur Build-Zeit nachsite/assets/theme/kopiert.
themeAssetUrl( "style.css" ) (oben verwendet, statt eines fest codierten
assets/theme/style.css) ist das, was das eigene CSS deines Themes
Content-Hash-Fingerprinting nutzen lässt -
assets.fingerprint (standardmäßig aktiv) bedeutet, dass jede Änderung,
die du an dieser Datei vornimmst, beim nächsten Build einen neuen
Dateinamen bekommt, sodass die Browser der Besucher nie eine veraltete,
zwischengespeicherte Kopie ausliefern. Das funktioniert genauso für jede
Top-Level-CSS- oder JS-Datei im eigenen assets/-Ordner deines Themes,
nicht nur für style.css - ein Theme mit einer eigenen assets/main.js
bindet sie auf dieselbe Weise ein:
<script src="themeAssetUrl( 'main.js' )"></script>.
moduleAssetUrl( "copy-code.js" ) ist derselbe Helfer für die eigenen,
themeunabhängigen, gemeinsam genutzten Skripte dieses Moduls (diejenigen,
die das eigene layout.bxm jedes integrierten Themes über
variables.moduleAssetsDir einbindet) - verwende ihn anstelle eines fest
codierten assets/copy-code.js, wenn dein eigenes Theme eines davon
direkt einbindet.
Beide Helfer fingerprinten nur eine Datei, die direkt in diesem Ordner
liegt - nie eine, die in einem Unterordner verschachtelt ist (ein eigenes
assets/fonts/ oder assets/vendor/ etwa). Das ist Absicht: Eine Datei,
auf die indirekt von einer deiner Top-Level-Dateien aus verwiesen wird
(ein @font-face url(...), ein CSS-@import), würde kaputtgehen, wenn
sie umbenannt würde, ohne dass auch jede Stelle, die unter dem alten
Namen darauf verweist, mit umgeschrieben wird - also rührt dies diese
Dateien niemals an. Wird einer der beiden Helfer für eine Datei
aufgerufen, die noch nicht existiert, oder bei deaktiviertem
assets.fingerprint, greift exakt der heutige, unveränderte Pfad als
Fallback - nie ein Fehler.
Jedes .bxm (integriert oder eigen), das stattdessen einen fest
codierten Pfad hart codiert, statt einen der beiden Helfer aufzurufen,
bricht dadurch nicht, bustet aber auch nicht automatisch den Cache - und
ein Build gibt jetzt eine einzeilige, nicht fatale Warnung aus, die die
betreffende Datei benennt, wenn genau das erkannt wird, damit ein solches
Theme nicht stillschweigend veraltet.