Loom is a dependency free, retained mode GUI layout engine written in pure Rust. Give it a tree of widgets and a container size and it computes the exact border box rectangle of every node with a flexbox style constraint solver, with optional wrapping. The core is headless, so it can run and be tested without a screen. Build a tree, set flex and spacing, and drag the size slider to watch every box reflow, live.
Each label shows #id kind and the computed rectangle x, y w × h. Click any box to select it. Green boxes are leaf widgets, blue boxes are containers.
every number above is computed live by the in-browser solver, the same algorithm as the Rust core. Draw calls are one rect per node plus one text call per label.
1, and it will
expand to fill the leftover space. Finally select any container and press
+ Text to drop a new widget inside it.
x, y for the top left corner and
w × h for the size, all in pixels.Loom separates the math from the pixels. Given a tree of widgets and a container size, it computes the exact border box rectangle of every node with a flexbox style constraint solver, with optional wrapping. There is no windowing crate, no font backend, no GPU. A Renderer trait is the only seam to the outside, so the same layout can drive a terminal, an SVG file, a canvas, or a GPU backend.
# a spacer absorbs all leftover space on the main axis, exactly: root.children[1].rect.w == 200.0 - 48.0 - 64.0 - 8.0 * 2.0 # golden layouts pin exact rectangles; invariants run over random trees # fed huge, fractional, and non finite sizes; a determinism gate proves # the same tree gives identical rectangles on every run
Correctness is committed as tests that run on every build. Every child rectangle must lie inside its parent content box on any axis that fits, siblings must never overlap, no coordinate may be non finite, and equal flex children must fill the main axis within a pixel. A self check even corrupts a good layout to prove the invariant checker rejects it, so a passing gate cannot be a blind gate.
This is what cargo run -- demo prints: the computed border box rectangle of every node, then the recorded draw calls, then a hit test probe. It is the same sample tree the playground starts with, so the numbers match box for box.
The spacer absorbs every pixel of leftover space on the toolbar row, so #4 lands at width 568. Drag the size slider in the playground and watch the same solver redistribute it.
Loom borrows its mental model from real layout systems. It does not try to replace them. It is the small, readable, headless version you can drop in with no supply chain and read end to end.
The spec Loom takes its rules from: main and cross axes, grow and shrink, justify and align, border box sizing. It lives inside a browser engine, not as a standalone headless crate you can single step.
Production layout engines. Yoga is Facebook's C based flexbox engine behind React Native. Taffy is a substantial Rust crate implementing flexbox, grid, and block. Fast, feature complete, and large.
The readable from-scratch version. A dependency free core that answers one question, a tree plus a size in and exact border box rectangles out, headless and deterministic, with a Renderer trait as the only seam. Small enough to read in a sitting, backed by golden and invariant gates.
Each of these is a real part of the solver in src/, not a marketing word.
Every rect is the border box: it includes border and padding but excludes margin, exactly like the CSS box model. A fixed width or height sets the border box size, and content lives inside border plus padding.
Leftover main axis space is shared by grow weight, and overflow is absorbed by shrink weight scaled by base size. Two equal grow children split the space evenly, one to three splits it one to three.
min_width, max_width, min_height, and max_height bound the resolved size on each axis. An unset bound means no limit, and when a minimum exceeds a maximum the minimum wins.
With wrapping on, children break onto a new line when the current line fills, an oversized child gets its own line, and justify and align apply per line, not once for the whole container.
The one seam to the outside. Layout emits draw calls to any Renderer, so the same computed tree can drive a terminal, an SVG file, a canvas, or a real GPU backend without touching the solver.
The same tree at the same size produces identical rectangles on every run, and any NaN or infinity handed in is normalized before the solver runs, so every coordinate the engine produces is finite.
Loom is a runnable CLI, a dependency free library with a fluent tree builder, and a correctness gate you can run yourself. The whole core is headless, so none of it needs a screen.
Print the computed rectangle tree at any container size, or run demo to also print the recorded draw calls and a hit test probe.
Build a tree with Node::row() and Node::column() and a fluent builder, then assign_ids, compute_layout, render to any Renderer, and hit_test or hit_path for input.
Golden layouts pin exact hand computed rectangles, invariants run over random trees fed degenerate sizes, a determinism gate proves stability, and a self check corrupts a layout to prove the checker is not blind. All green under cargo test.
# print the computed rectangle tree at 800 x 600 cargo run -- 800 600 # also print draw calls and a hit test probe cargo run -- demo # run the correctness gates and unit tests cargo test cargo clippy --all-targets -- -D warnings LOOM_FUZZ_OPS=5000 cargo test invariants