Variables & Magic Functions
On this page
Variables & Magic Functions
Two small, related features for keeping repeated facts and repeated logic
out of your Markdown: reusable variables, defined once in
bxsites.yaml and dropped into any page with {{ }}, and magic
functions, small BoxLang helpers you write once in docs/functions.bxs
and call the same way - everywhere, with no import, no plugin, no wiring.
Both share one syntax:
{{ dotted.path }} # a reusable variable
{{ $name(arg1, arg2) }} # a magic function call
Reusable variables
Add a variables block to bxsites.yaml - any shape you like, flat or
nested:
variables:
company: "Ortus Solutions"
product:
name: "BoxLang"
supportEmail: "support@example.com"
{
"variables": {
"company": "Ortus Solutions",
"product": {
"name": "BoxLang",
"supportEmail": "support@example.com"
}
}
}
Then reference any of it, by dotted path, from any Markdown page:
# Welcome to {{ company }}
We build {{ product.name }} tools. Need help? Write us at
{{ product.supportEmail }}.
builds to:
<h1>Welcome to Ortus Solutions</h1>
<p>We build BoxLang tools. Need help? Write us at support@example.com.</p>
A {{ }} variable is resolved once, at build time, against whatever
bxsites.yaml's own variables block has right then - rename a product,
update a support address, or bump a year in one place, and every page
using it picks up the change on the next build. See
variables in the configuration
reference.
Magic functions
Add a docs/functions.bxs file (or src/functions.bxs, if your project
uses src/ - see Getting Started) - a plain
BoxLang script. Any function you name with a leading $ becomes a magic
function: callable from {{ }} in Markdown, and callable bare, directly,
from a project's own theme/
.bxm overrides.
function $shout( text ) {
return uCase( arguments.text ) & "!"
}
function $badge( label, kind = "info" ) {
return '<span class="badge bg-' & arguments.kind & '">' & arguments.label & '</span>'
}
{{ $shout('this is important') }}
Status: {{ $badge('Stable', 'success') }}
builds to:
<p>THIS IS IMPORTANT!</p>
<p>Status: <span class="badge bg-success">Stable</span></p>
A magic function can return anything toString()-able - plain text,
HTML, a number - and it's spliced straight into the page's Markdown
before it's converted, so returning real HTML (like $badge() above)
works exactly the way you'd expect.
A function declared without a leading $ in the same functions.bxs is
just a private helper, meant to be called only from your other
$-prefixed functions in the same file (they're all loaded into the same
scope, so one can call another bare) - {{ }} can never call one directly
(only a $name(...) call target is ever recognized), and it isn't part of
the documented public surface a theme override should call either, even
though it happens to be technically reachable there too:
private string function formatPrice( amount ) {
return "$" & numberFormat( arguments.amount, "9.99" )
}
function $price( amount ) {
return formatPrice( arguments.amount )
}
Calling a magic function from a theme override
Because a magic function is bound directly into template scope, a
project's own theme/page.bxm (or layout.bxm) can call it bare, with no
prefix at all - the same way it already reads variables.page/
variables.siteConfig:
<p class="build-banner">#$shout( 'built with boxlang' )#</p>
Context variables
Every magic function's own body can also read a fixed set of "supporting
variables" - bare, with no argument needed - regardless of whether it's
called from {{ }} in Markdown or bare from a theme override:
| Variable | What it is |
|---|---|
siteConfig | The site's own bxsites.yaml config (already defaulted/validated) |
page | The current page (see the note below - not every field is populated yet when called from Markdown) |
nav | This tree's own nav tree |
basePath | Root-relative base path, ending with / |
versions | Version switcher entries - [ { label, url } ] |
currentVersion | Which versions entry is being rendered right now |
locales | Language switcher entries - [ { code, label, url, dir, flag } ] |
currentLocale | Which locales entry's code is being rendered right now |
currentLocaleDir | "ltr"/"rtl" for the current locale |
function $sitename() {
return siteConfig.name
}
function $pagetitle() {
return page.title
}
Site: {{ $sitename() }}
Page: {{ $pagetitle() }}
page isn't equally complete in both places. Called from Markdown,
page is this specific page's own struct as loaded from disk -
title/description/tags/icon/summary/ogImage/urlPath/
relativePath/body/etc. are already there, but the fields only known
once every page in the tree has finished converting - toc,
prevPage/nextPage, breadcrumbs, editUrl/lastUpdated, iconHtml,
markdownUrl, canonicalUrl - don't exist on it yet. Called bare from
page.bxm, page is the fully-enriched struct, all of those included.
Every other supporting variable (siteConfig, nav, basePath,
versions, currentVersion, locales, currentLocale,
currentLocaleDir) is identical in both places.
Argument syntax
A magic function call's arguments are simple, comma-separated literals or variable references - no nested function calls or expressions in this first version:
- Numbers:
{{ $discount(20) }} - Quoted strings:
{{ $greet('World') }}or{{ $greet("World") }} - Booleans:
{{ $badge('Beta', true) }} - A
{{ }}-less dotted variable reference:{{ $greet(product.name) }}
Visualizer recipes
A magic function returning HTML isn't limited to a status badge - it's a
general-purpose way to get GitBook-style visual cells (a star rating, a
colored chip, a progress bar) without GitBook's own database-backed column
picker, which bx-sites' git-based, plain-Markdown source has no equivalent
of. The four below are this site's own
docs/functions.bxs,
rendering live on this exact page.
Ratings
function $stars( required numeric rating, numeric max = 5 ) {
var filled = min( max( round( arguments.rating ), 0 ), arguments.max )
var stars = repeatString( "★", filled ) & repeatString( "☆", arguments.max - filled )
return '<span title="' & arguments.rating & ' out of ' & arguments.max & '" style="color:##f5a623;letter-spacing:2px">' & stars & '</span>'
}
`{{ $stars(4) }}` renders as: ★★★★☆
Status chips
function $badge( required string label, string kind = "info" ) {
var palette = {
"info" : { "bg" : "##e0edff", "fg" : "##1d4ed8" },
"success" : { "bg" : "##dcfce7", "fg" : "##15803d" },
"danger" : { "bg" : "##fee2e2", "fg" : "##b91c1c" },
"warning" : { "bg" : "##fef9c3", "fg" : "##854d0e" }
}
var pick = palette.keyExists( arguments.kind ) ? palette[ arguments.kind ] : { "bg" : "##f1f5f9", "fg" : "##475569" }
return '<span style="display:inline-block;padding:0.1em 0.6em;border-radius:999px;font-size:0.85em;font-weight:600;background:'
& pick.bg & ";color:" & pick.fg & '">' & encodeForHTML( arguments.label ) & "</span>"
}
`{{ $badge('Stable', 'success') }}` renders as: Stable - and `{{ $badge('Beta', 'info') }}`: Beta
Progress bars
function $progress( required numeric percent ) {
var pct = min( max( arguments.percent, 0 ), 100 )
return '<span style="display:inline-block;width:120px;height:8px;background:##e5e7eb;border-radius:999px;overflow:hidden;vertical-align:middle"><span style="display:block;height:100%;width:'
& pct & '%;background:##2563eb"></span></span> ' & pct & "%"
}
`{{ $progress(72) }}` renders as: 72%
Trend indicators
function $trend( required numeric value ) {
var isUp = arguments.value >= 0
var arrow = isUp ? "â–²" : "â–¼"
var color = isUp ? "##16a34a" : "##dc2626"
var sign = isUp ? "+" : ""
return '<span style="color:' & color & ';font-weight:600">' & arrow & " " & sign & numberFormat( arguments.value, "0.0" ) & "%</span>"
}
`{{ $trend(4.2) }}` renders as: â–² +4.2% - `{{ $trend(-1.8) }}`: â–¼ -1.8%
Inside a table cell
{{ }} resolves against the raw Markdown before tables
are even parsed, so any of the above work inside a pipe table's cells the
same as anywhere else on the page - the closest thing here to GitBook's own
Select/Rating table columns:
| Feature | Status | Rating |
| --- | --- | --- |
| Dark mode | {{ $badge('Stable', 'success') }} | {{ $stars(5) }} |
| Table sort | {{ $badge('Beta', 'info') }} | {{ $stars(4) }} |
Which renders as:
| Feature | Status | Rating |
|---|---|---|
| Dark mode | Stable | ★★★★★ |
| Table sort | Beta | ★★★★☆ |
Showing the syntax literally
A {{ }} shown inside a fenced code block (three backticks or more, like
every example on this page) is left completely untouched rather than
resolved - the same convention this module already uses for $...$ math
and === "Tab" content tabs. Unlike those two, a {{ }} shown in inline
code (`{{ example }}`, single or double backticks) is protected too -
every bullet point above showing `{{ $discount(20) }}` inline is a
real, working example of that.
A {{ }} whose contents don't look like either a variable path or a
$name(...) call - some other templating engine's own {{ }} syntax
shown in prose, say - is left untouched rather than treated as an error.
Only a token that looks like a variable or a magic function call, but
doesn't resolve, fails the build (see Errors below) - that's
deliberate, to catch a real typo without misreading unrelated {{ }} text
as broken syntax.
Scope
functions.bxsis project-wide - one file, loaded once, the same set of magic functions available on every page across the main tree and every version/locale tree. You don't need to duplicate it intodocs/versions/<name>/ordocs/i18n/<code>/.variablesis likewise a single, project-widebxsites.yamlblock - it isn't itself translatable per locale. A multilingual project wanting different variable text per language can instead reach for a magic function that switches onsiteConfig.i18n.defaultLocale.code(or simply keep the value locale-neutral - a product name, a support email).
Reserved names
A theme/page.bxm/layout.bxm override calling a magic function bare
($name(...)`) works because every loaded function - `$-prefixed or a
private helper alike - is bound directly into that same template's own
rendering scope, right alongside the built-in variables.page/
variables.siteConfig/etc. that every theme already reads. That means a
functions.bxs function sharing a name with one of those already has one:
avoid page, nav, siteConfig, themeDir, basePath,
moduleAssetsDir, versions, currentVersion, locales,
currentLocale, currentLocaleDir, strings, requiredFiles and
stringsResolver for a private helper's own name (a $-prefixed magic
function can never collide with any of these, since none of them start
with $).
Errors
BxSites.UnknownVariable- a{{ dotted.path }}(or a$name(...)argument that looks like a variable reference) doesn't match anything inbxsites.yaml'svariablesblock.BxSites.UnknownFunction- a{{ $name(...) }}call doesn't match any$-prefixed function indocs/functions.bxs.BxSites.InvalidFunctions-docs/functions.bxsfailed to load (a BoxLang syntax error in the file itself).BxSites.InvalidConfig-bxsites.yaml'svariableskey is present but isn't an object.