How do I…?

Short answers. Each one is a thing you write and the page you get back. Nothing here needs the pages before it — jump straight to what you want.

…put a value on the page

Two braces. Whatever is between them comes from your data.

Hello {{ customer.name }}, your total is {{ order.total }}.
<p>Hello Alice, your total is £59.97.</p>

The value is always made safe for you. If customer.name is Tom & <Co>, the page shows Tom & <Co> as text — never as markup, never as a broken tag. You do not have to remember to do anything.

Now change it: misspell the field, {{ customer.nme }}. You get told, before a page exists. Most template languages print nothing there and ship the page.

…show a list of things

:for: line in order.lines
  - {{ line.sku }} × {{ to_string(line.qty) }}
:!for:
<ul>
  <li>APPLE × 3</li>
  <li>PEAR × 1</li>
</ul>

Everything between :for: and :!for: repeats once per item, and line is the name you chose for each one.

…show something only sometimes

:if: order.has_discount
  You saved {{ to_string(order.saved) }}.
:!if:

Nothing appears when there is nothing to say. Same shape as the list — that is on purpose, and it is the only shape you need to learn.

…reuse a piece of a page

Write the piece once, in its own file, and say what it needs at the top:

:props: title: String, body: String
:!props:

## {{ title }}

{{ body }}

Then use it by name from anywhere:

:card: title="Pricing" body="From £9 a month."
:!card:

A .bmx file is a component. There is no separate idea to learn — the same document you were already writing becomes reusable the moment it says what it needs.

…put a class or an id on something, for CSS

:card: title="Pricing" .featured .wide #plans
  Any **markdown** in here.
:!card:
<div class="featured wide" id="plans">
  <p>Any <strong>markdown</strong> in here.</p>
</div>

.name is a class, #name is an id. Use as many classes as you like and one id — an id is an address, and two of them means the page cannot answer where is it.

That is all Tailwind, Bootstrap or your own stylesheet needs. See Styling the output for the full list of tags you get.

…make a navigation bar

A bar is a brand and a row of links, and both are ordinary markdown — so you write it in the document, not in a template around it:

# Roast&Co

[Coffee](/coffee) [Kit](/kit) [Search](/search) [Sign in](/signin) [Basket · 2](/basket)
<h1>Roast&amp;Co</h1>
<p><a href="/coffee">Coffee</a> <a href="/kit">Kit</a> … <a href="/basket">Basket · 2</a></p>

The stylesheet does the rest: .bmx > h1 is the brand and the first p is the links. The front page is exactly this — one document, no HTML, and the buttons and search box are links with a border-radius.

It works with no framework, and it is positional, so it moves if you add a paragraph above it. When that matters, name the region with a block — :nav: .bar — and your host decides what it renders as. Styling the output has both versions side by side.

…put a block on one line

Say where the head stops, with -> [ … ], and the rest of the line is the body:

:button: -> [on:click=save(id), .featured, #plans] Save :!button:
<button class="featured" id="plans">Save</button>

Without the brackets there is no body. :button: on:click=save(id) Save :!button: puts Save in the head, because the format cannot tell where your attributes stopped and your text began — and then a host reading a bare word as a boolean attribute drops it silently. The brackets are you saying which is which.

The body is real content, so this works:

:span: -> [class=price] **{{ order.total }}** :!span:

The ** is emphasis and the slot is escaped, both by BMX. That is the difference from passing the text as an attribute — an attribute value is a string the format never looks inside.

Two rules worth knowing, and both are refusals rather than surprises:

  • The head ends at the first ]. If a value of yours needs a ], use the form without brackets — it takes the whole line, so there is no delimiter to collide with.
  • A body needs the line to close. :b: -> [x] body with :!b: further down is refused: the body would have two sources and no reader could tell which won.

…leave a note that does not appear on the page

<!-- ask a designer whether this row should be sortable -->
Totals

Nothing between <!-- and --> reaches the page, and it may span as many lines as you like.

One rule: a comment is a whole line. Total: 5 <!-- fix this --> is refused, and that refusal is the point — before 0.12 the format had no comment at all, so a note written mid-sentence was delivered to the reader as visible text. Put the marker at the start of its own line, or use a code span to show the characters literally: `<!-- x -->`.

…react to a click

:button: on:click=save(line.id)
  Save
:!button:

The on:click=… part is handed to whatever is rendering your page, untouched. BMX does not know what a click is — which is why this works the same whether your page is rendered by Burxt, JavaScript, or something nobody has written yet.

…show money without it changing behind my back

Write the conversion in the document, where a reviewer can see it:

Total: {{ to_string(order.total) }}

If order.total is exact to two decimal places, that is what appears. If the number you hand it has more places than the page can show, you are asked to say what should happen rather than having it rounded for you.

This is the part that is only true in Burxt. Other renderers will show the number; Burxt refuses to guess.

…write something that looks like BMX, without it being BMX

Put it in a code block. Nothing inside one is read as markup:

```
This {{ stays }} exactly as written.
```

That is how these pages document BMX in BMX.

For a single character in ordinary text, put a backslash in front of it: \*not emphasis\*.

…read a document from a file instead of typing it inline

Do this rather than putting a document in a string:

match file_read_maybe("receipt.bmx") {
    None => { print("no document"); }
    Some(source) => { /* … */ }
}

Burxt uses { … } inside strings for its own purposes, so a document pasted into one confuses the two. Files avoid it, and .bmx files are what you want anyway.

…find out why it refused

Read the message — it says where and what:

BMX-E002 at 16: unterminated strong

at 16 is how many characters into your file the problem starts. Every code and its cause is on When it refuses, each with the exact input that produces it and the fix beside it.

…see the whole thing at once

Writing a document is the cheat sheet — every construct with the HTML it produces, on one page.

If you have not written a document yet, start with Your first document instead. It takes five minutes and you get a page at the end of it.