Blocks
Everything you can write, on one page.
The shape
::: name head
body
:::
A block opens with :::, closes with :::, and the body is an ordinary document — headings,
paragraphs, lists, and more blocks.
props — what the component is given
::: props name: String, total: Decimal<2>, lines: [Line]
:::
Always the first block. Empty body.
| Kind | For |
|---|---|
String |
text |
Int |
whole numbers, counts |
Bool |
true or false |
Decimal<2> |
money |
[Thing] |
a list |
The first prop is the component’s state — the thing your buttons change.
Elements — wrapping things
::: section
# Today
Three things happened.
:::
Elements come in three kinds, and the kind decides what may go inside.
Text-level — holds text and nothing else:
a b button caption code em h1–h6 i label legend mark option q s
small span strong sub summary sup textarea title u
Empty — holds nothing at all:
area base br col embed hr img input link meta source track wbr
Everything else — holds anything:
address article aside blockquote canvas dd details dialog div dl dt
fieldset figcaption figure footer form header li main nav ol p picture
pre section select table tbody td tfoot th thead tr ul video
Put a heading in a text-level element and you are told — that is HTML’s rule, not star-burxt’s, and browsers handle the invalid version unpredictably.
Attributes
Anything before an on: is an attribute:
::: div class=card id=main
hello
:::
A value with spaces is quoted — class="tag muted". A bare name is a boolean attribute —
::: input disabled. And a value can interpolate:
::: a href=/posts/{{ to_string(post.id) }}
read more
:::
The expression is checked like every other one, so a typo is a compile error rather than a broken link.
on: — events
::: button on:click=count + 1
increment
:::
::: input on:input=name
:::
Most DOM events: the pointer and mouse, keydown/keyup, the form events including focus and
blur, dragging, touch, wheel, scroll, and the animation and transition events. Ask for one
that is not wired and the message says what to write instead.
Every event carries a value: the typed text for input, the key for keydown, the coordinates
for a pointer, the animation’s name for animationend. It reaches a handler as value.
After = goes an expression for the next value — or a message, in a component with a ===bx
section — and it runs to the end of the line, so write the event last if the block has anything
else on it.
for — a row per item
::: for line in lines key line.id
::: li
{{ line.label }}
:::
:::
key is required in practice: it is how a row is recognised when the list changes. Use something
that identifies the item.
No on: handlers inside a for yet.
match — choosing between many
::: match model.route
::: case Home
# Welcome
:::
::: case Post(id)
# Post {{ to_string(id) }}
:::
:::
A match holds case blocks and nothing else. A case’s head is a pattern, and it binds — id is
in scope inside that branch.
Every variant must have a branch. Add one to the enum and forget its case, and the build
fails naming it. See chapter 4.
else — the other branch
::: if ready
…
:::
::: else
…
:::
Directly after the if, nothing between them, and no condition of its own.
if — a section that appears when it should
::: if ready
::: p
Everything is set.
:::
:::
Any condition that answers true or false. No else — write a second if.
A component — a capitalised name
::: Badge amount={{ model.count }} tone=unread
:::
Any .sbmx you imported in ===bx:
===bx
use "./Badge.sbmx";
===
Props are passed by name. Order does not matter; a missing one is named.
{{ }} — a value in the page
Hello {{ name }}, you have {{ to_string(unread) }} messages.
Text goes in as it is. Everything else needs to_string.
Markdown
Everything a document normally has, inside blocks or outside them:
# Heading
## Smaller heading
A paragraph with **bold**, *italic*, `code` and a [link](https://example.com).
- a list
- of things
1. or a numbered
2. one
> A quote.