Looks like TypeScript because it is
The TS 5 TSX grammar plus contextual declarations. Models already know it: 64 blind programs had zero syntax-caused errors.
table page api crud layout
state effect authLith is TypeScript, JSX and Prisma-style queries plus five declarations. An AI agent builds a correct, full-stack web app in about a quarter of the tokens it spends on Next.js, and the compiler keeps every colour and size on your design system.
table Todo own { title: string(1..200) done: boolean = false created: Date = now() } page "/" { const todos = db.todo.findMany({ orderBy: [{ done: "asc" }] }) return <main> <form action={addTodo}> <input name="title" /><button>Add</button> </form> <ul>{todos.map(t => <li> <input type="checkbox" checked={t.done} onChange={() => setDone(t.id, !t.done)} /> {t.title} </li>)}</ul> </main> } function addTodo(title: string) { db.todo.create({ data: { title } }) } function setDone(id: int, done: boolean) { db.todo.update({ where: { id }, data: { done } }) }
Six reference tasks, each written by an expert in Lith and in five popular web stacks: Next.js, SvelteKit, Laravel, Django and Rails. Tokens are counted with the o200k_base tokenizer: what a model actually pays.
Write tokens are the mean of 6 expert solutions per stack (Lith: the v0.2.4 reference solutions). Files and lines count everything written for a task, starting from each framework's standard scaffold. The blind result: fresh models that had only the 2,402-token Lith card built 4 hidden apps in each of 5 runs, 20 of 20 passing after compiler-guided fixes (v0.2 release check).
A todo app with per-user data, validation, a detail page and a 404. In Lith, the schema, pages, server logic and the server/client split all live together, and the compiler infers which code runs where.
table Todo own { title: string(1..200) done: boolean = false created: Date = now() } page "/" { const todos = db.todo.findMany({ orderBy: [{ done: "asc" }, { created: "asc" }] }) return <main> <h1>Todos</h1> <form action={addTodo} class="flex-row"> <input name="title" /> <button>Add</button> </form> <ul> {todos.map(t => <li> <input type="checkbox" checked={t.done} onChange={() => setDone(t.id, !t.done)} /> <a href={`/todos/${t.id}`} class={t.done ? "line-through" : ""}>{t.title}</a> <button onClick={() => deleteTodo(t.id)} class="text-danger">Delete</button> </li>)} </ul> </main> } page "/todos/:id" (id: int) { const todo = db.todo.findUniqueOrThrow({ where: { id } }) return <main> <h1>{todo.title}</h1> <p class="text-muted">Created {todo.created}</p> </main> } function addTodo(title: string) { db.todo.create({ data: { title } }) } function setDone(id: int, done: boolean) { db.todo.update({ where: { id }, data: { done } }) } function deleteTodo(id: int) { db.todo.delete({ where: { id } }) }
Lith came out of a measured design lab: four competing designs, blind tests with models that had never seen the language, and a rule that every feature must lower total cost.
The TS 5 TSX grammar plus contextual declarations. Models already know it: 64 blind programs had zero syntax-caused errors.
table page api crud layout
state effect authRanges and lengths live in the type and are checked on every write and every form. Migrations for new columns are automatic.
title: string(1..200)
views: int(0..) = 0own scopes every query, update and delete to the logged-in user. Other users' rows act as missing.
table Note own { body: string }A function that touches db or me runs on the server; the browser calls it automatically. No "use server", no API glue.
<form action={addComment}>Inputs fill typed parameters by name. Every check runs at once, and failures re-show the form with messages next to each field. It works without JS.
fail({ body: "No links" })List, get, create, patch and delete with validation, JSON errors and correct status codes. Plain api routes return any Response.
crud "/api/products" Productstate and effect replace hooks. Pages without islands ship 0 kB of JavaScript; the client runtime targets 10 kB gzipped.
state starred = task.starredlith check returns the first error as JSON with a ready patch, so a fix costs one short round instead of a debugging session.
{ "code": "E0110", "patch": … }Emits standard JS, HTML and CSS on Node 22. Import any npm package; export .d.ts types so TypeScript can call into Lith.
import { marked } from "marked"Every Lith app takes its look from a design system: a standard token file with primitives and semantic tokens. The rules are in the compiler, not in a prompt, so a model follows them in markup, CSS and server code alike, and every violation comes back with the fix.
Hex colours, px sizes, arbitrary [values] and Tailwind's palette are errors in classes and in <style> alike. Each error carries a safe patch to the nearest semantic token, measured as colour distance (ΔE00) or px.
The palette and scales exist only as targets of semantic tokens. App code names roles (surface, text-muted, danger), never blue-700, so a rebrand is a token-file edit.
Code cannot declare tokens or set CSS variables. A token added to the file that nearly equals an existing one (ΔE00 < 2, within 1px) is an error, and lith check lists every design-system change on its own.
Emails, OG images, PDFs and API responses style themselves from tokens.color.text, never a literal. Raw colours and lengths in CSS strings and style objects are E0620 there too.
A data slot lets the database set a brand colour per tenant. The compiler derives its on-colour, always at least 4.5:1, so text on the brand colour meets WCAG AA.
W3C Design Tokens and Figma variable exports load as they are: any names, light, dark, high-contrast or brand modes. Output is plain CSS variables; switching modes needs no JavaScript.
Token counts: o200k_base · 6 measured tasks · 5 baseline stacks · blind validation n = 8