Chext

By Steph19
Scroll to open

Chext

The format this page is written in. A block starts at any line whose first character is a colon and runs until the next one. There is no closing tag, no indentation rule and no fence. That is nearly the whole specification.

The Two Chips Below Were Written Like This

Six lines. What you see under the code block is what those six lines produced, on this page, in this render.

plaintext
 :NOTE
 A note is one block. Every line inside it joins into a single string.
 |
 :INFO
 The Chip Under the Note
 Line one becomes the title. Everything after it becomes the body.

A note is one block. Every line inside it joins into a single string.

The Chip Under the Note

Line one becomes the title. Everything after it becomes the body.

Why That Code Block Is Indented

Because a line whose first character is a colon starts a new block, and the parser applies that rule inside a code block too. To quote chext markers verbatim in chext, every marker line needs a leading space it does not otherwise have. This is a genuine limitation of the format, discovered the first time someone tried to document the format in the format, and it is left in rather than special-cased. A parser with an escape hatch for its own documentation is a parser with a special case in it forever.

There Is No Regular Expression

The block parser is ninety-four lines of string index arithmetic. It splits on newline, strips trailing whitespace only, and asks whether the first character is a colon. Leading whitespace is deliberately preserved, which is what makes indented code survive. Anything appearing before the first marker is discarded without comment, which is why a file can start with a note to yourself.

How an Attribute Is Read

Find the first opening bracket on the marker line, find the first closing bracket, split what is between them on commas, split each piece at its first colon. Lowercase the key, keep the value exactly as written. Two consequences follow directly and neither is worked around: an attribute value can never contain a comma, because a comma always ends it, and it can never contain a closing bracket. If you need either, the value belongs in the body.

Two Columns, and the Pipe Decides

A marker suffixed with .half asks for half width. A block whose last non-blank line is a single pipe character is linked to the one after it. Rows are two columns wide and that number is hardcoded. The important part is that adjacency is not what pairs two halves: the pipe is. Two half blocks written one after another with no pipe between them land in two separate rows, each half empty. The next code block and the pair beneath it are the same seven lines again.

plaintext
 :INFO.half
 Left
 The pipe on the line below belongs to this block.
 |
 :INFO.half
 Right
 Which is why these two share a row.

Left

The pipe on the line below belongs to this block.

Right

Which is why these two share a row.

What the Positions Mean

The type is everything before the first dot, and half is true if .HALF appears anywhere in the suffix. So .half, .HALF, .big.half and .half.big all give you a half chip, and .big on its own gives you a full one. Unknown modifiers are ignored rather than rejected, so a typo costs you a layout and not a page.

Chip types in the vocabulary

43

Types with a chext builder

29

Grid columns

2

Maximum input length

200,000 chars

Issue severities

2

The Failure Model, Precisely

Three outcomes, and they are not equally loud. A bean-validation failure drops that one chip and records a warning, so a title one character over its limit makes the chip vanish quietly and the rest of the page render fine. An unknown chip type records an error and skips the block. And an exception thrown inside a builder is caught by nothing at all: it propagates out and the entire slate fails to sync. Worth saying plainly: no authoring mistake currently reaches that third level. Every enum lookup in the twenty-nine builders is wrapped and falls back, and the Stats icon that used to fail a whole slate now resolves to a default instead. The path is open and nothing walks it today.

The quiet one is the dangerous one. A chip that silently is not there looks identical to a chip you forgot to write.

March 2026, Forty-One Files

The parser used to live in the browser. On the thirteenth of March 2026 it moved to the server in a single commit: forty-one files changed, two hundred and eighty-nine lines added, four hundred and ninety removed. An eighty-nine line client parser was deleted and a chext block was stripped out of eighteen separate chip definition files. The reason was one source of truth. A repo sync and a paste into the import box now go through identical code, so a file that works in one works in the other.

What the Move Cost

The old browser parser stripped inline comments: everything after a double slash on any line, marker or content, was cut. The server parser only recognises a comment when the line begins with one. The documentation screen still describes the old behaviour, with an example whose output would now contain the comment it promises to remove. A behaviour was dropped and its documentation was not, which is a normal thing to happen and an abnormal thing to leave standing for four months.

The Format Only Goes One Way

There is a serializer that turns chips back into chext, and it exists solely to serve the .chext URL extension. It is not used for editing, and the editor never round-trips through text. That asymmetry has consequences you can observe: Image, Gallery and Portfolio serialize to a bare marker with no content, and any type the serializer does not handle falls through to the same empty result. A Journey chip serialized and re-parsed comes back with zero entries because the two sides disagree on the entry format. A task in progress serializes to a task that is done. Round-tripping was never a requirement, so it was never true, and calling it a bug would be generous to the original plan.

Twenty-Nine of Forty-Three

Fourteen chip types are declared in the enum with no builder behind them. They cannot be written from a file and never could be. The gap between a vocabulary and an implementation is usually hidden; here it is one enum away from anyone who looks.

"

A format small enough to hold in your head is a format you will still be able to read in three years.

"