Syntax
The opjsx markup grammar — elements, classes, attributes, nesting, and text.
A opjsx template is indentation-based. Each element is one line; its children are the more-indented lines beneath it. There are no closing tags.
Elements
Every element line starts with &:
&div
&header
&my-custom-elementThe & marker is what distinguishes an element from text, so any tag name works —
including custom elements. A line that does not start with & is treated as a text child.
Classes
Use Emmet-style dot shorthand for className:
&div.card
&div.card.active
&button.btn.btn-primary&div.card.active → <div className="card active">. Dots separate classes; hyphens
live inside a class name (&div.nav-container is one class, nav-container).
Attributes
Attributes are name"value" — no = sign:
&input type"text" placeholder"Search..." name"q"
&a href"/about"Values are typed: tabIndex"3" → number 3, hidden"true" → boolean true.
A .class shorthand and a className"…" attribute can coexist; they merge.
Nesting
Indentation defines the tree. No closing tags:
§ion.hero
&div.content
&h1
Big title
&p
Some copyText children
Any indented line that doesn't start with & is a text node:
&h1
Welcome to the site
&p
A paragraph of plain text.Putting it together
&article.post
&h2.post-title
Hello
&div.post-body
&p
First paragraph.
&p
Second paragraph.renders to:
<article class="post">
<h2 class="post-title">Hello</h2>
<div class="post-body">
<p>First paragraph.</p>
<p>Second paragraph.</p>
</div>
</article>Next: making it dynamic with Interpolation.