Home
| Colors: |
January 2026

HTML Tags as Template Tokens

Or, The Reinvention of Cold Fusion

Okay. Hear me out...

Templates, but they're HTML tags.

Crazy. I know.

But, it just might work.

How It Started

I use MiniJinja1 for my static site generators. It's the Rust version of Jinja2. It's fantastic.

I don't like the default tokens for tags, expressions, and comments. That's not a problem. MiniJinja lets you change them. I switch out:

{{ }}
{% %}
{# #}

with:

[@ @]
[! !]
[# #]

The sharp edges of the square brackets are easier for me to track and read.

Where I'm Coming From

I spend a lot of time thinking about how to make it easier for folks to make stuff on the web. Eventually, all the static site generators I build for myself will be ret-coned into prototypes for a site builder designed for other folks. Specifically, folks who are interested in having a web site and are willing to tinker a little.

I think about them every time I feel friction making pages. The one I'm feeling most stems from templates. There are three practical sets of tokens:

HTML Tags
<div>show something</div>
MiniJinja Tags
[! include "something" !]
MiniJinja Expressions
[@ output_something @]

A nice, clean separation of concerns. Great on paper. Tedious in practice.

Which One Goes Where Now?

The frequency with which I use:

[! !]

when I should have used:

[@ @]

is high. Doubly so when you include missing in the other direction.

You get used to it after a while. Error messages lead to instant fixes. A quick bump. Usually it doesn't break the flow. Except for when it does.

The question becomes: why not eliminate the issue all together.

Express Thyself With Tags

Expressions output the result of whatever is inside a pair of these:

[@ @]

For example:

[@ some_var @]

[@ some_method() @]

Tags kick off with a keyword inside a pair of these:

[! !]

For example:

[! set ... !]

[! include ... !]

The complete list of tags is: for, if, extends, block, include, import, with, set, filter, macro, call, do, autoescape, raw, break, continue.

What if we added one more?

[! output !]

Passing it the same content from:

[@ something @]

Would become:

[! output something !]

Sure:

[! output some_variable !]

is longer than:

[@ some_variable @]

But, it's an easy trade. Eliminating the mental overhead of having to remember when to use ! instead of @ and vice verse is well worth the few extra characters.

While We're At It

Why stop there? If going from three sets of tokens to two is good, then going from two to one is great. Let's drop the square brackets all together and use the < and > from HTML.

In theory, we could use <set ...>, <include ...> etc... That's a bad idea. Our names might collide with native HTML elements (<output>, for example, already does).

The smart folks in charge of HTML have already provided us with a solution. Tag names with - characters in them. They provide a namespace that is guaranteed not to collide with native HTML.

Where I'm Going

My current though is to use an x character to extend the tag name. Something like <x-for> would work. I'd flip it, though. It reads better as <for-x>.

Using that approach, this:

<ul>
  [! for file in files !]
    <li>
      [@ file.name @]
    </li>
  [! endfor !]
</ul>

turns into this:

<ul>
  <for-x file in files>
    <li>
      <output-x file.name>
    </li>
  </for-x>
</ul>

I need to play around with the idea. Poking at it with a working prototype. But, just looking at a single token approach compared to the triple thread, it feels like a good direction to explore.

-a

end of line

Endnotes

Yes, I just re-invented Cold Fusion.

(Ok. Not really, but you could be forgiven for thinking so.)

There is one place this falls down. Template items placed inside other HTML elements. For example, this would work, but it looks weird and feels janky:

<div <output-x "something">>
  content
</div>

But, you could always fall back to the standard template tokens:

<div [@ something @]>
  content
</div>

Or the single token approach where everything is a template tag:

<div [! output something !]>
  content
</div>

That's the way I'll go at the start.

I'm pretty sure I can do a first, naive implementation of this with just some basic parsing and converting. Things like escaped characters can wait for a later prototype.

The new -x tags will be valid HTML in some cases. Not in all of them though (e.g. I don't think <output-x "something"> is valid). Validity doesn't matter for the most part. The template engine will parse and transform the tags before they hit the browser.

The place it does matter is in the editor. Syntax highlighting will Just Work™ in most cases. Tweaking an existing HTML tool to do even better would require way less work than creating/maintaining one the uses any variation of jinja style tags.

Footnotes