Why a custom markup language for landing pages

A reasonable question, asked often: why a custom markup language for landing pages? Why not just use MDX? Or Markdown? Or a YAML schema? Or React components, like everyone else?

Short answer: I tried all of those, and each one failed in a specific way for the use case Micropage is built for — landing pages authored by humans and AI agents, on the same primitives, with predictable output.

The use case is narrower than it looks

A landing page isn’t a blog post. It isn’t a documentation page. It isn’t a web app. It’s a small, predictable shape: a hero, two or three feature columns, social proof, pricing, FAQ, a CTA. Most landing pages look the same — not because designers are unimaginative, but because that shape converts.

If the shape is fixed, the markup that produces it can be small. Smaller than HTML. Smaller than MDX. Smaller than a templating engine.

What didn’t work

MDX gives LLMs too much rope. I tried using MDX as the source-of-truth and asking Claude to author landing pages. It works — sort of. The model invents JSX components, references libraries that don’t exist, mixes markdown and React in inconsistent ways. You spend half the time cleaning up component imports. The output is impressive but not reliable.

Markdown is too small. No layout primitives. No columns. No call-to-action buttons that map to design tokens. You can extend Markdown — that’s what MDX is — but now you’re back to the previous problem.

YAML schemas are unergonomic. Strict typed schemas are great for machines, but humans hate hand-editing them. Indentation as syntax is fragile. You quickly end up writing tooling on top of YAML to make it editable, and at that point you’ve reinvented a markup language anyway.

JSX / React components are overkill. A landing page doesn’t need state. It doesn’t need hooks. It doesn’t need a build step or a JS runtime. Asking the user to write <Section><Column><Heading>... is asking them to write code when they wanted to write prose.

What we built instead

A line-oriented markup. One element per line. A closed vocabulary. Predictable section blocks. The whole grammar fits in a parser that’s small enough to keep in your head.

[site]
title: Lantern
description: Let people know you're safe while traveling
logo: <- lantern-logo.svg

[nav]
Home -> /
How it works -> /how-it-works

/// section align:center
col:
  h1: Check in once. Reduce worry for everyone.
  p: A simpler way to let people know you're safe while traveling.
  btn-secondary: See how it works -> /how-it-works

That’s a landing page. No imports, no components, no escape hatches by default. If you need free-form HTML, there’s a /// html fence — but you have to opt in, and the agents we send through this don’t reach for it.

The accessibility angle

I built this in part because drag-and-drop builders aren’t accessible to me. I’m visually impaired; panels move, controls shrink, every quarter a builder rearranges itself for “user research.” Plain text doesn’t do that. A .page file in vim is the same on Tuesday as it was on Monday.

Markup is also screen-reader-friendly in a way that visual builders fundamentally aren’t. That matters.

The agent-authoring angle

Once you decide humans want a small markup, you also unlock the AI-authoring angle as a side effect. The same constraints that make the markup easy to hand-edit make it reliable for LLMs to generate:

  • Closed element vocabulary → no invented elements.
  • Line-oriented → no nesting bugs.
  • Constrained design tokens → no inline styling guesses.
  • No free-form HTML → no script injection.

Claude 4.7 generates correct Micropage markup on the first try, every time, without any fine-tuning or schema prompting. We just include a short reference in the prompt and it works.

The trade-offs

A custom DSL has real downsides, and I’d be lying if I said it was the obvious right call.

  • No ecosystem. No Astro components. No React. No NPM packages. If you want a fancy interactive widget, you write it as an HTML fence or you don’t have it.
  • A learning curve, even if it’s small. “Just use HTML” is a more familiar entry point.
  • We have to ship every feature. If you want a video gallery, we have to add a video: element. There’s no ejecting into JSX.
  • Less visual freedom. The opinionated single template means you can’t reproduce arbitrary designs from Dribbble. We think that’s fine for landing pages, but it’s not for everyone.

When you should not use a constrained DSL

If you need pixel-perfect control over every layout decision, use Framer. If you need a full design system, use Webflow or build your own React app. If you need a documentation site with dense interactive components, use MDX + Astro/Docusaurus.

For small, focused landing pages where you want consistency, predictability, AI-authoring, and zero hosting decisions — a tiny markup language is the right shape.

Takeaway

A DSL isn’t always the right answer, but for landing pages it solves a real problem: it produces a substrate that both humans and AI agents can author confidently, without inventing structure that breaks the page. Five hundred lines of parser, one opinionated template, and the rest is just writing.

If you want to try it, the free tier ships a real site on a *.micropage.sh subdomain.