I've been building MDL — a tiny authoring language that compiles to clean HTML. If you haven't seen it yet:
page:
hero:
## Sign in
Welcome back.
form@id(loginForm)@submit(handleLogin):
field:
label:
Email
.input@type(email)@id(email)@required
actions:
.btn-primary@id(loginBtn)(Sign in)
.btn-ghost@click(handleForgot)(Forgot password?)
Compiles to clean, semantic HTML. No angle brackets written by hand. CSS owns all layout. JS handles all logic.
Install: npm install -g @tosiiko/mdl
What's already shipped in 0.1.x
Before getting into what's coming — here's what's already working:
- Indentation-based sections that compile to semantic HTML (
page:→<main>,nav:→<nav>,form:→<form>) - Full CommonMark Markdown inside every section — headings, lists, tables, code blocks, blockquotes
- Dot inline elements —
.badge(text),.btn-primary(text),.input@type(email) - Attributes —
@id(value),@required,@placeholder(text),@submit(fn),@click(fn) - CSS bundling — multiple CSS files merged into one
app.css - JavaScript event wiring —
@submit(handleLogin)compiles to a properaddEventListener - Live reload dev server —
mdl serveatlocalhost:3999 - Multi-page projects via
mdl.json -
mdl initandmdl new— scaffold a full project in one command -
mdl checkandmdl format— lint and auto-format - VS Code extension with syntax highlighting — already on the marketplace
- Prebuilt Rust binary distributed via npm — no Rust toolchain needed
That's 0.1. Here's what's coming next.
Source maps
Every compiled HTML page will write a JSON sidecar map:
pages/login.mdl → dist/login.html
→ dist/login.html.mdlmap.json
The map links every generated HTML element back to the MDL source line that produced it:
{
"version": 1,
"source": "pages/login.mdl",
"output": "login.html",
"mappings": [
{
"generated": { "line": 12, "column": 1, "end_line": 31, "end_column": 8 },
"source": { "line": 6, "column": 3, "end_line": 6, "end_column": 39 },
"kind": "section",
"name": "form"
}
]
}
This unlocks editor navigation, error overlays with exact MDL line and column, and eventually go-to-source from generated HTML back to the .mdl file that produced it — the same way browser source maps let you debug TypeScript instead of compiled JS.
A project-level dist/mdl-manifest.json links every route to its source and source map:
{
"route": "/login",
"source": "login.mdl",
"output": "login.html",
"source_map": "login.html.mdlmap.json"
}
Document metadata
Right now metadata lives in mdl.json. The next release formalises and extends this with a complete metadata system:
{
"lang": "en",
"title": "My Site",
"description": "A small MDL site.",
"canonical": "https://example.com/",
"favicon": "assets/favicon.svg",
"social": {
"enabled": true,
"image": "assets/social-card.png",
"twitter_site": "@mysite"
}
}
The social preset derives Open Graph and Twitter card tags automatically from title, description, and canonical. One config, full social sharing coverage:
<meta property="og:title" content="My Site">
<meta property="og:description" content="A small MDL site.">
<meta property="og:image" content="assets/social-card.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@mysite">
mdl check validates all local file references — favicon, social image, manifest links — at build time, not when a user finds a broken image in production.
Better diagnostics
mdl check is getting significantly smarter. The next release adds:
Accessibility checks:
- Missing
alton images - Missing
titleon iframes - Duplicate IDs
- Unlabeled form controls
- Invalid or empty
aria-*attributes - Missing ARIA references
- Landmark ordering issues
Form checks:
- Label and input pairing mistakes
- Missing label targets
- Ungrouped radio buttons
- Missing autocomplete hints
Asset checks:
- Broken
.link()and.href()targets - Missing local images and media files
- Recursive CSS
url()and@importvalidation - Font file references
Machine-readable output:
mdl check --json
Outputs structured diagnostics with 1-based line and column ranges — ready for editor integrations, CI pipelines, and language server consumers.
Multi-app workspaces
One install, multiple apps:
mdl init --app admin --port 4001
mdl serve apps/admin
Each app owns its own mdl.json, pages, CSS, and scripts. They share the same MDL install and environment. Useful for projects with a public site and a separate admin panel, or a marketing page and a docs site.
Advanced browser primitives
The next release adds first-class MDL sections for:
canvas: → <canvas> WebGL, charts, custom drawing
frame: → <iframe> embedded content
picture: → <picture> responsive images
progress: → <progress> loading bars
meter: → <meter> scalar measurements
template: → <template> inert DOM templates
slot: → <slot> web component slots
island: → <div> hydrated JS island host
component: → <div> custom component host
With @mount(handler) wiring for JS initialisation:
canvas@id(scene)@mount(drawScene):
Compiles to a <canvas> that calls drawScene() from your configured scripts on mount.
What I'm building toward
MDL's goal is simple: you should be able to build a complete, production-quality web page without writing a single HTML tag. Structure in .mdl. Design in .css. Logic in .js. The compiler handles everything else.
The next release gets closer to that by making the tooling smarter — better errors, better source maps, better metadata, better workspace support.
Try it now
npm install -g @tosiiko/mdl
mdl new my-site
cd my-site
mdl serve
- npm — https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2
- VS Code — https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language
- GitHub — https://github.com/tosiiko/mdl-code
Would love to hear what you think — and what you'd want to see in MDL before you'd use it on a real project.
Top comments (0)