BMX

Write a page that cannot be quietly wrong

Burxt Markup Language, Extensible. You write markdown. You get a page.

What this shows
roastandco.example/shop

Roast&Co

Coffee Kit Brewing Search Sign in Basket · 2

Coffee, kit and grinders

Free shipping over £40. Everything roasted the week it ships.

  • Aeropress Go — £39.00 · brews in two minutes, packs into its own mug
  • Comandante C40 — £229.00 · the grinder people keep for a decade
  • Ethiopia Guji — £14.50 · blueberry, jasmine, a long finish
"The only coffee maker I take on a plane."

Shop all kit

shop.bmx the whole page, and the only file
# Roast&Co

[Coffee](/coffee) [Kit](/kit) [Brewing](/brewing) [Search](/search) [Sign in](/signin) [Basket · 2](/basket)

## Coffee, kit and grinders

Free shipping over **£40**. Everything roasted the week it ships.

- **Aeropress Go** — £39.00 · brews in two minutes, packs into its own mug
- **Comandante C40** — £229.00 · the grinder people keep for a decade
- **Ethiopia Guji** — £14.50 · blueberry, jasmine, a long finish

> "The only coffee maker I take on a plane."

[Shop all kit](https://example.com/kit)

The bar is the heading and the row of links — the first two lines of the document. BMX has no attribute syntax, so the stylesheet reaches them by position rather than by class: .bmx > h1 is the brand, the first p is the links. Brittle on purpose, to show where the seam is — :nav: is how you name the region instead, and your framework decides what it renders as.

The content is markdown doing what markdown does: a heading, a paragraph, a list, a quote, a link. The cards are list items and the thumbnails are CSS gradients — BMX has no image syntax (SPEC.md §7).

One document, 15 lines, and no HTML anywhere — the bar, the search box, the buttons, the cards, the quote, the price. A document and a stylesheet, which is what every page has.

The difference from markdown is that BMX tells you when the document is wrong, instead of printing something that looks nearly right.

# Receipt {{ order.reference }}

Thank you, **{{ customer.name }}**. Your total is {{ order.total }}.

- Delivery: {{ order.delivery }}
- Paid: {{ order.paid_at }}

That is a whole document. If you have written a README you can already write BMX — the {{ … }} holes are the only new idea, and page 2 of the guide is about those.

And when a page needs to repeat, branch, or be built from reusable pieces, there is exactly one more thing to learn:

:for: line in order.lines
  - {{ line.sku }} × {{ to_string(line.qty) }}
:!for:

One construct — a block — which also gives you :if: and components you declare yourself. BMX does not know what for means; the host does.

Start here

1. Your first document — write one, render it, see the HTML that comes out. Five minutes.

Then:

Or just look up what you need

   
In your editor colour, a live preview, and mistakes underlined as you type
How do I…? short answers — a list, a component, a class for CSS, a click
Writing a document every construct, with the HTML it produces
Styling the output the tags you get, and how CSS, Tailwind and SCSS attach
When it refuses every message, with the input that causes it and the fix

Why it exists

Two reasons, and neither is the syntax.

It tells you when a document is wrong

Markdown never fails. Whatever you give it, something comes out — which is lovely in a comment box and dangerous when the document was generated, truncated, or written in a hurry.

Your balance is **£240.00

Markdown prints that with two stray asterisks and the page ships. BMX says:

BMX-E002 at 16: unterminated strong

Autocorrect quietly changes what you meant. Spellcheck stops and points. BMX is spellcheck.

The blanks can be checked before the page exists

{{ … }} is a hole, and BMX deliberately has no opinion about what goes in it — that belongs to whatever language is doing the rendering. Which means a language with types can check it:

  What happens to a typo like {{ order.custmer }}
Most template languages prints nothing; the page ships
BMX, rendering refuses; nothing renders
BMX, compiled a build error naming the field, and what the fields actually are

A template is the last place in most programs where nothing is checked. The database checks your data, the API checks it, your code checks it — and then the template prints whatever it is handed. That is the gap BMX closes.

In Burxt it goes further: a money value keeps its rounding rule all the way to the tag, so a template cannot quietly round £4.947525 to two places on your behalf.

Can you rely on it?

Your document means the same thing in every tool that reads BMX, and that is checked rather than promised — more than ninety documents paired with exactly what each should produce, plus two independent implementations run against each other.

BMX is 0.12, and honest about what that means: both implementations have one author, so they catch mistakes and not ambiguity. Why documents agree is the short version, and it says plainly what would have to happen for this to be 1.0 — which is an implementation by somebody who did not write the rules. If that sounds like you, writing an implementation is a suite that takes any command and an afternoon.

Free to use, MIT or Apache-2.0.