Pretext Lab
29Pretext + flexbox
Arc 10 · Ecosystem & Future

Pretext + flexbox

A layout is more than one paragraph. A real UI is boxes inside boxes — a header beside a sidebar beside a body, each with text at the leaves. Pretext tells you how tall a paragraph is at a given width. Pair it with a tiny flex solver and you can compute the whole tree's geometry — every box's x, y, width, and height — entirely in JavaScript, before anything mounts.

Drag the width slider. Five nested boxes re-arrange and re-wrap in lockstep: Pretext measures each text leaf; the solver distributes space along the main axis and stacks the column along the cross axis. No DOM reflow is ever triggered — these are SVG-like rectangles whose positions come from arithmetic.

5 nodes · 3 text leaves
560

Mechanism

The tree is a plain JS object: a row container wrapping three children — a text leaf on the left, a column of three text leaves in the middle, and a text leaf on the right. Each text leaf carries a string; each container carries a direction (row / column) and a list of children with flex weights.

At boot we call prepareWithSegments(text, font) once per text leaf — the handles are reusable across every width the slider will ever produce. On each slider event we run a pure-JS solver:

The solver is one recursive pass. For this tree it runs in microseconds. We then stamp absolutely-positioned <div>s at the computed coordinates — one assignment per node, no getBoundingClientRect, no offsetHeight.

Application

Once layout is arithmetic, a whole category of things becomes easy:

Inspired by Textura (Pretext × Yoga) by Charlie Greenman. This demo implements a tiny subset of the pattern — Textura is the full library, backed by Facebook's Yoga engine.

"A sentence, well set, ends by giving back what it was given. A room, well arranged, does the same. Each part is held by its neighbors, and the whole is held by its parts — form containing form, and no part anywhere that does not know its place."

Contemplative register

Direct Claude

"measure the whole layout, no DOM" combine Pretext with a userland flex engine; recurse the tree in JS "know positions before mounting" solve the tree; stamp absolute coordinates; zero reflow on mount "layout-aware AI without a browser" Pretext in Node + a flex solver; ask "does it overflow" offline "speculative sizing each frame" layout() is cheap — try multiple widths, pick the winner
Next: the same paragraph rendered two ways. One stays text; one only looks like text.
a minimal flex solver over Pretext handles
import { prepareWithSegments, layout } from '@chenglou/pretext';

// Tree: each node is either a text leaf (handle + flex) or
// a container (direction + children with flex weights).
const tree = {
  kind: 'container', direction: 'row', flex: 1,
  children: [
    { kind: 'text', flex: 2, handle: prepareWithSegments(T1, FONT) },
    { kind: 'container', direction: 'column', flex: 3, children: [
      { kind: 'text', flex: 1, handle: prepareWithSegments(T2, FONT) },
      { kind: 'text', flex: 1, handle: prepareWithSegments(T3, FONT) },
      { kind: 'text', flex: 1, handle: prepareWithSegments(T4, FONT) },
    ]},
    { kind: 'text', flex: 2, handle: prepareWithSegments(T5, FONT) },
  ],
};

// Solve: recurse the tree, writing x/y/w/h onto each node.
function solve(node, x, y, w, h) {
  node.x = x; node.y = y; node.w = w; node.h = h;
  if (node.kind === 'text') return;
  const total = node.children.reduce((s, c) => s + c.flex, 0);
  if (node.direction === 'row') {
    let cx = x;
    for (const c of node.children) {
      const cw = (c.flex / total) * w;
      solve(c, cx, y, cw, h);
      cx += cw;
    }
  } else {
    let cy = y;
    for (const c of node.children) {
      const ch = (c.flex / total) * h;
      solve(c, x, cy, w, ch);
      cy += ch;
    }
  }
}

// Text-leaf height comes from Pretext, not the DOM.
function measureLeafHeight(node, lineHeight) {
  return layout(node.handle, node.w, lineHeight).height;
}