--- name: bx-sites-themes metadata: version: "1.0" description: Choose, customize, override, install, or write a theme for a bx-sites (ortus-boxlang/bx-sites) site - the 10 built-in themes, air-gapped/offline considerations, the ThemeProvider contract (layout.bxm/page.bxm, plus optional search.bxm/blog.bxm/blog-page.bxm and any frontmatter-named layout), the layout: frontmatter key (outer shell + body), bootstrap's marketing homepage (home.bxm), color-only customization via extraCss, ejecting/overriding a theme, writing one from scratch (template variables, shared helpers like bxsitesNavHref/bxsitesIcon, which shared scripts to load), the page-actions dropdown and image gallery chrome, installing a published theme from ForgeBox, importing an mkdocs/jekyll/hugo theme, and the homepage hero banner. Use this whenever a user wants to change a bx-sites site's look, brand colors, or write/override its templates. --- # BxSites Themes Reference Themes are native BoxLang `.bxm` templates - no separate template engine or build step. ## Built in | Theme | Base | Notes | |---|---|---| | `bootstrap` (default) | Bootstrap 5, vendored | Poppins font, brand gradient navbar | | `material` | Hand-rolled Material-style CSS | Card layout, elevation shadows, Roboto | | `tailwind` | Tailwind Play CDN | Utility-class driven, no build step, **not air-gapped** | | `docsy` | Forked from `material` | Read the Docs/Docsy-inspired navy reference-manual look | | `slate` | Forked from `material` | Stripe/Slate-inspired - permanently dark sidebar | | `docusaurus` | Forked from `material` | Bold full-width colored navbar, rounded cards | | `justthedocs` | Forked from `material` | Minimalist; search box at top of sidebar | | `vuepress` | Forked from `material` | Green accent, soft rounded corners | | `gitbook` | Forked from `material` | Centered reading column, serif headings | | `notion` | Forked from `material` | Borderless sidebar, near-grayscale, generous whitespace | The seven `material`-forked themes reuse `material`'s exact `.bxm` templates unchanged except a scoped CSS-class-prefix rename (and, for `justthedocs`, one relocated `` line) - same full feature set, same air-gapped-capable behavior. `bootstrap`/`material`/`tailwind` share the BoxLang brand palette (`#00FF78 -> #00DBFF` gradient); their accents differ (`bootstrap` `#FFF500`, `material` `#00C853`, `tailwind` `#38bdf8`). The seven gallery themes each use their own distinct palette. Every theme ships the same feature set regardless of palette: in-page TOC, breadcrumbs, prev/next links, highlight.js code blocks with copy buttons, self-hosted webfonts, dark/light toggle (Alpine.js, `localStorage` remembered), responsive header + collapsible sidebar, `/` and Cmd/Ctrl+K search shortcuts, repo/edit-page/last-updated line, Download-Markdown link, opt-in footer, version switcher, themed `404.html` (override with `docs/404.md`), custom logo/favicon, collapsible nav, Google Analytics, social share cards, page tags/icon/summary, nav override support, extra CSS/JS injection, admonitions/footnotes/definition lists, content tabs, code annotations, responsive images, Mermaid, math - see `bx-sites-markdown` and `bx-sites-content-blocks` for that content-side syntax, and `bx-sites-configuration` for every key mentioned here. Recent chrome, in all 10 themes unless noted (config keys, including every `theme.options` key such as `accentColor`/`accentColorDark` - a no-CSS alternative to the `extraCss` recipe below - live in `bx-sites-configuration`): - **Page actions dropdown** - `pageActions: true`, placed by `theme.options.pageActionsPosition` (`top`|`bottom`). Items: Copy page, Copy link, View as Markdown, Open in ChatGPT/Claude, Edit on GitHub, Export as PDF, Share on X/LinkedIn/Facebook. Each item only appears when the page has what it needs: Markdown items need `page.markdownUrl`; copy link, AI and share items need `page.canonicalUrl` (set `baseURL`); Edit needs `page.editUrl`. - **"Report an issue"** link in the page-meta row when `repo.url` is set. - **Prev/next cards**, and **course lesson nav** (badge plus prev/next cards) when the page belongs to a `::: course` (`page.course`). - **TOC** - `theme.options.tocPosition`: `top` (default) or `sticky` (side rail). - **Click-to-enlarge images** - `imageGallery: true` ships a lightbox for every content image (opt out per image with `class="no-zoom"`). - **Locale dropdown** when `i18n` locales exist; **"Powered by BxSites"** badge, unconditional (`bxsites-logo.svg`). ```yaml title="bxsites.yaml" theme: { name: material } ``` ## Installing a published theme ```bash bxSites install:theme --name=bx-sites-theme-blog1 [--version=1.0.0] ``` Downloads from ForgeBox into `/.themes/bx-sites-theme-blog1/` (inside the project's resolved content root, not the bare project root - see "Resolution order" below), validating the `ThemeProvider` contract before finishing (a broken package fails at install time, not at the next `build`). Re-installing the same name deletes the existing `.themes//` first, with no prompt (unlike `theme:import`, which merges - see below). No separate activation step (unlike a plugin - see `bx-sites-plugins`) - just set `theme.name` to match. Browse published themes under ForgeBox's `bxsites-themes` category. ## Air-gapped / offline sites Works with zero internet access by default for `bootstrap`, `material`, and the seven gallery themes, with the default `local` search provider (see `bx-sites-search`) - Bootstrap CSS/JS, highlight.js, Alpine.js, MiniSearch all vendored (`resources/assets/vendor/`), no CDN tag anywhere. Turning on `mermaid` vendors it the same way. Still reach the network only when turned on: `tailwind`'s utility engine (CDN JIT compiler - not air-gapped-capable); Mermaid's `elk`-layout diagrams lazy-load one chunk from jsDelivr; `math` loads KaTeX from a CDN; `searchProvider.provider: algolia` and `analytics.provider: google` inherently talk to a hosted endpoint. For a genuinely air-gapped deployment: stick to `bootstrap`/`material`/a gallery theme, the `local` search provider, avoid `elk`-layout Mermaid, leave `math`/Algolia/analytics off. ## The `ThemeProvider` contract A theme is a folder with: - **`layout.bxm`** (required) - outer HTML shell + nav. Receives `variables.page`, `variables.nav`, `variables.siteConfig`, `variables.themeDir`, `variables.basePath` (root-relative, ends `/` - prefix every internal `href`/`src` with it rather than hardcoding a leading `/`). Includes the resolved body template via `#variables.themeDir#/#variables.bodyFile#` (`page.bxm` unless a page/section resolves to something else - see "Multiple layouts per page" below). - **`page.bxm`** (required) - article body. Renders `variables.page.contentHtml` (already-converted markdown). - **`search.bxm`** (optional) - search box markup, included only when `search: true`. - **`blog.bxm`** (optional) - an alternate outer shell for the blog's own listing/category/archive/author/stats pages. Falls back to `layout.bxm` when a theme doesn't have it. - **`blog-page.bxm`** (optional) - an alternate body for an individual blog post, rendered under the theme's normal `layout.bxm`. Falls back to `page.bxm` when a theme doesn't have it. - **`assets/`** (optional) - theme CSS/JS, copied to `site/assets/theme/`. - **`slots/.bxm`** (optional) - default content for a slot the layout renders with `renderSlot( "" )` - see "Slots" below. - **`theme.json`** (optional) - manifest: `{ "slug", "name", "slots": [...] }` - see "Theme manifest" below. - **`.bxm`** (optional) - any extra template a page can opt into by frontmatter `layout: ` - `home.bxm` is the built-in example (see "Multiple layouts per page" below). Not enforced; a name no theme file matches simply falls back. Also available in every template (a from-scratch theme can rely on all of these; missing optional values are defaulted to empty): - `variables.page` always carries: `title`, `contentHtml`, `toc` (`[ { level, id, text } ]`), `prevPage`/`nextPage` (`{ title, url }`), `breadcrumbs`, `course` (`id` is `""` when the page isn't a lesson), `editUrl`, `lastUpdated`, `description`, `tags`, `icon`, `iconHtml`, `summary`, `ogImage`, `markdownUrl`, `canonicalUrl` (empty without `baseURL`), `urlPath`, `isFallbackContent`. - `variables.siteConfig` (`repo`, `social`, `footer`, `theme.options`, ...), `variables.moduleAssetsDir`, `variables.data` (data files), `variables.locales` / `currentLocale` / `currentLocaleDir` (`ltr`|`rtl`, for ``), and `variables.strings` (the locale's UI labels, e.g. `strings.toggleDarkMode`; `variables.stringsResolver.format()` fills placeholders in them). - Project `$`-prefixed magic functions are bound bare, like the helpers below. - `variables.versions` (`[ { label, url, kind } ]`, `kind` one of `"current"`/`"released"`/`"next"` - see `bx-sites-blog-versioning-i18n`) + `variables.currentVersion`. Order depends on `versions.default`: unset, it is "Latest" (`current`) then released versions newest first; set, it is that version (`current`), the other released versions, then "Next" (`next`) last. Shared includes (`` and `.../locale-flags.bxm`): - `bxsitesIcon( name )` - raw SVG for `github`, `twitter`/`x`, `rss`, `youtube`, `linkedin`, `facebook`, `bluesky`, `threads`, `slack`, `patreon`, `email`, `edit`, `clock`, `markdown`, `globe`, `moon`, `sun`, `copy`, `link`, `print`, `sparkle`, `chevron-down`, `flag`, `outline`; an unknown name returns the generic globe. - `bxsitesUrlAttr( value )` - escapes only `&`, `"`, `<` (keeps `/` intact) for a double-quoted attribute. - `bxsitesNavHref( url, basePath )` - the `href` for a nav node: prefixes `basePath` to docs-relative URLs, keeps absolute ones (`https:`, `mailto:`, `//host`), turns `javascript:`/`data:`/`vbscript:` into `#`, and attribute-escapes. Always use it for nav links instead of concatenating. - `bxsitesLocaleFlag( code )` (locale-flags.bxm) - flag emoji for a locale code. Three more helpers are bound into every theme's render scope (built-in or custom) the same way, no include needed: - `themeAssetUrl( "style.css" )` / `moduleAssetUrl( "copy-code.js" )` - resolve a theme's own top-level `assets/` file (or one of this module's shared scripts) to its basePath-prefixed, cache-busted URL. Use these instead of a hardcoded `assets/theme/...`/`assets/...` path for anything a layout links directly - only top-level files are fingerprinted, never one in a subdirectory. - `renderVersionSwitcher( variables.versions, variables.currentVersion )` - builds the whole version-switcher `