paged.web — HTML and CSS as print content

The web, set in print

InDesign can't speak web; web tools can't speak print. paged.web lays out HTML and CSS inside frames, flows it across pages, and bakes it to native items InDesign opens.

AGPL-3.0 OR PMEL·canary — usable, moving fast·0.1.0-canary.12·github.com/paged-media/plugin-web ↗

123 / 156
layout verdicts identical to Chrome
39
hand-written fixtures replayed against Chrome
7 / 7
flows across threaded frames agree
0
lines of page JavaScript run, by design
Layout — a browser engine, not an approximation

A web frame is laid out by a browser stack

Insert a web frame, or open an .htmlfile, and the frame’s content is HTML and CSS. The Blitz engine — Stylo for styles, Taffy for boxes, Parley for text — is compiled to wasm and lays the source out at the frame’s size. The result is drawn inside the frame on the editor canvas: fills, strokes, text runs, linear, radial and sweep gradients, box shadows and images.

The source lives in the document itself: in the frame’s metadata label when it fits, otherwise in a content-addressed part of the .paged container. It is undoable and travels through IDML.

The Paged Annual, page 94: a web frame and the envelope the document keeps — the HTML and CSS printed beside it. The frame prints empty here: the live render is session state and is not stored in the file.
The Paged Annual, page 94: a web frame and the envelope the document keeps — the HTML and CSS printed beside it. The frame prints empty here: the live render is session state and is not stored in the file.
Source panel
HTML and CSS editors, a sandboxed preview at the frame's size, a linter, font diagnostics and a tag outline.
Document faces
Fonts the CSS names are registered from the document, so bold and italic shape in their own faces.
Overflow policies
Clip, shrink to fit, grow the frame, or continue into a thread.
Fragmentation — CSS break rules across threaded frames

One source, threaded through as many frames as it needs

Thread a web frame into further frames and the flow render lays the remainder out again at each frame’s width, cutting between blocks, between lines of a paragraph, between children of a container and between table rows — with a <thead> repeating in every frame.

break-before, break-after, break-inside, orphans and widows are honoured. A frame is a page box, so @page margins inset every frame. CSS flow-into names several flows, and each goes to its own frames.

/* one source, two flows */
article { flow-into: article-flow; }
aside.route-notes { flow-into: sidebar-flow; }

h2 { break-after: avoid; }
figure, table tr { break-inside: avoid; }
p { orphans: 2; widows: 2; }

@page { margin: 6mm; }   /* every frame is a page box */
The fragmentation properties the flow render reads from the source.
The Paged Annual, page 95: one source, two named flows, four frames. The chain is saved in the document; the flow renders are session layers, so the printed page shows the frames and their routing, not the paint.
The Paged Annual, page 95: one source, two named flows, four frames. The chain is saved in the document; the flow renders are session layers, so the printed page shows the frames and their routing, not the paint.
Bake — from a web render to page content InDesign opens

When the layout is right, make it ordinary page content

“Bake web frame to document” turns a rendered frame or a whole flow into native swatches, gradient swatches, rectangles, paths, image frames and text frames, in one undoable step. Strokes, translucency and a rectangle’s drop shadow are kept.

The result exports to IDML and PDF like anything else on the page. InDesign opens it with every item, face, stroke, opacity, shadow, path and image where the bake put it — checked by the InDesign lane of the plugin’s conformance suite. What the bake cannot carry, it counts and reports.

The Paged Annual, page 97: a web frame baked to native rectangles, paths, swatches and text frames, with the bake's own receipt beside it — including what it could not lower.
The Paged Annual, page 97: a web frame baked to native rectangles, paths, swatches and text frames, with the bake's own receipt beside it — including what it could not lower.
Editing and data

Type into the render; bind it to the document

In-frame text editing
Double-click the frame, click rendered text, and a caret lands in the DOM text node it was painted from. Typing re-renders live; Enter writes the source back as one undo step.
Template data binding
{{doc.title}}, {{doc.pages}}, {{frame.page}} and {{data.<field>}} from a paged.data provider are substituted on every render, and the canvas re-renders when a value changes.
Outline to canvas
Click a tag in the panel's outline and its painted box is outlined on the canvas.
Import
An .html file opens as a new web frame: <style> becomes the CSS, <body> the HTML, and the sanitiser runs on it.

Page JavaScript never runs — not in the preview (a sandbox with no permissions) and not on the canvas. Templates have no expressions. A print document should lay out the same way every time it opens.

Parity — recorded, not asserted

123 of 156 layout verdicts identical to Chrome

Thirty-nine hand-written fixtures and seven flows are replayed in CI against recordings made in Chrome. 123 of 156 layout verdicts agree, and every flow agrees. The 13 open defects sit in the pinned layout engine — line-height: normal, multi-column, vertical-align, collapsed table borders, position: sticky and others — and each is written up for the upstream project.

Live — the real editor, booted as paged.web

Set some HTML in print

paged.web
The paged editor, booted as paged.web. It runs in this tab.open full screen ↗
Spec sheet — from the registry, not from marketing

paged.web features

Plugin · web · 10
Bake to native content — flatten a rendered web frame/flow to Paged page items (IDML/PDF export path)
plugin-web.bake-to-native
experimental· untested
On-canvas vector rendering of web frames (Blitz/WASM → C-1 sceneLayer lane)
plugin-web.engine-rendering
stable· 2 tests
Flow fragmentation — re-line-break one flow across N frames (ADR-020 ladder)
plugin-web.flow-fragmentation
stable· 1 test
Flow threading — one web source across a chain of publication frames
plugin-web.flow-threading
stable· untested
.html file importer — File/Open + drag-drop an HTML file into a web frame
plugin-web.html-importer
stable· 1 test
Insert web frame (one undo step, frame + source metadata)
plugin-web.insert-command
stable· 15 tests
Source persistence in the document (IDML round-trip, foreign-open safe)
plugin-web.metadata-persistence
stable· 13 tests
Flow/render readout — the last render/flow/bake outcome in the source panel
plugin-web.render-readout
stable· 2 tests
WebFrameSource model + metadata envelope + diagnostics linter
plugin-web.source-model
stable· 7 tests
Source panel — HTML/CSS editors, sandboxed preview, options, diagnostics
plugin-web.source-panel
stable· 13 tests

10 features · commit-pinned snapshot · the same rows the team plans by