Language
Syntax
The whole language on one page: blocks, props, code, loops, components and styles.
This page shows all of ArcMoon in short examples. Each part links to its own page for the details.
Blocks
A block is an HTML element: [name] opens it, [end] closes it, and ! means it has no body.
[section]
[h1]Users[end:h1]
[p]Three [strong]active[end] users.[end]
[hr!]
[end:section]<section>
<h1>Users</h1>
<p>Three <strong>active</strong> users.</p>
<hr>
</section>
More in Blocks and Self-closing blocks.
Props
Props go after = as name: value. On an element they become attributes:
[a = href: "/users", class: "link"]All users[end]
[input = type: "email", required: true, disabled: false !]
[p = css.color: "teal", --gap: "8px"]Styled[end]<a href="/users" class="link">All users</a>
<input type="email" required>
<p style="color: teal; --gap: 8px">Styled</p>
More in Props.
Comments
# one line
###
many lines
###
[p]Shown[end]<p>Shown</p>
More in Comments.
Escaping and raw text
A backslash makes [, # and ${ plain text. arcm-raw: true writes a whole body as it is:
[p]Write \[p] for a paragraph, \#1 for a number.[end]
[pre = arcm-raw: true][b] and # stay as written[end]<p>Write [p] for a paragraph, #1 for a number.</p>
<pre>[b] and # stay as written</pre>
More in Escaping and Raw text.
Build-time code
${ }$ runs in Node.js while the page is built. Only its result goes into the HTML:
${ const types = ["string", "number", "boolean"]; }$
[p]ArcMoon props can be ${ types.join(", ") }$.[end]<p>ArcMoon props can be string, number, boolean.</p>
More in Build-time code.
Loops
[for-each] writes its body once for each item; i is the position:
${ const users = [{ name: "Adam", role: "admin" }, { name: "Eid", role: "editor" }]; }$
[ul]
[for-each = ${ users }$, as: "user"]
[li = data-role: ${ user.role }$]${ i + 1 }$. ${ user.name }$[end]
[end]
[end]<ul>
<li data-role="admin">1. Adam</li>
<li data-role="editor">2. Eid</li>
</ul>
More in Loops.
Runtime code
runtime ${ }$ runs in the visitor's browser. With a signal, the page updates by itself:
runtime ${
import { signal } from "arcmoon/reactive";
const count = signal(0);
}$
[button = onclick: runtime ${ () => count(count() + 1) }$]Clicked runtime ${ count() }$ times[end]More in Runtime code and Reactivity.
Components
A component is another .arcm file. [import] names it, ArcMoon.props() reads its props, and [slot!] holds the body it is given:
${ const { name, role = "member" } = ArcMoon.props(); }$
[article = class: "user"]
[h3]${ name }$ [small]${ role }$[end][end]
[slot!]
[end]
[style]
.user { padding: 8px 12px; border: 1px solid #ddd; }
[end][import = UserCard: "./UserCard.arcm" !]
[UserCard = name: "Adam", role: "admin"]
[p]Joined in 2026.[end]
[end:UserCard]<style>.user[data-a-ctppmt]{padding:8px 12px;border:1px solid #ddd}</style><article class="user" data-a-ctppmt>
<h3 data-a-ctppmt>Adam <small data-a-ctppmt>admin</small></h3>
<p>Joined in 2026.</p>
</article>
More in Components and Built-in blocks.
Styles
A [style] is CSS: global in a page, scoped to the component in a component (like UserCard above). css. and -- props style one element. More in Styles.
Lines with no output
A line that only holds things with no output is left out of the HTML entirely, with its indent and line break, so imports, comments and setup code leave no blank lines:
[import = Card: "./Card.arcm" !]
# a comment
${ const n = 2; }$
[main]
[p]${ n }$[end]
[p]a blank line above stays[end]
[end]<main>
<p>2</p>
<p>a blank line above stays</p>
</main>
- Things with no output:
[import], comments, a top-levelruntime ${ }$block, and a${ }$that doesn't end with a value (like${ const n = 2; }$). - Blank lines you write between elements are kept.