Skip to main content

Vivliostyle

Vivliostyle is an open-source CSS typesetting engine (AGPL-3.0) built on the full CSS Paged Media specification. It covers the same core features as Paged.js — running headers, cross-references, facing pages — and additionally supports complex book layouts, EPUB output, and vertical writing modes.
Unlike Paged.js, which transforms the document in-place with a single script tag, Vivliostyle uses a viewer approach: you provide a content HTML file and a separate wrapper page that loads the engine. PolyDoc captures the rendered output from that wrapper.

Setup

Content document

Keep all CSS Paged Media rules in the content HTML file. This is the document Vivliostyle will typeset.
<!-- content.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4; margin: 2.5cm 2cm;
@top-center { content: string(chapter); font-size: 9pt; color: #888; }
@bottom-center { content: counter(page) " / " counter(pages); font-size: 9pt; }
}
h1 { string-set: chapter content(); break-before: page; }
</style>
</head>
<body>
<h1 style="break-before: auto">Introduction</h1>
<p>Your content here...</p>
</body>
</html>

Viewer page

The viewer page loads the Vivliostyle Core engine and signals PolyDoc when rendering is complete via a data-* attribute.
<!-- viewer.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>body { margin: 0; } #viewer { width: 100%; height: 100vh; }</style>
</head>
<body>
<div id="viewer"></div>
<script src="https://unpkg.com/@vivliostyle/core/lib/vivliostyle.min.js"></script>
<script>
const viewer = new vivliostyle.CoreViewer({
viewportElement: document.getElementById("viewer"),
});
viewer.addListener("readystatechange", () => {
if (viewer.readyState === "complete") {
document.body.setAttribute("data-vivliostyle-ready", "true");
}
});
viewer.loadDocument("https://your-server.com/content.html");
</script>
</body>
</html>

API request

{
"source": "https://your-server.com/viewer.html",
"render": {
"waitForFunction": "document.body.getAttribute('data-vivliostyle-ready') === 'true'",
"mediaType": "screen"
},
"layout": {
"preferCSSPageSize": true
},
"timeout": 120
}

Features

Dynamic headers

Vivliostyle supports both string-set / string() for text-only content, and the running() / element() approach that pulls a full DOM element — including images or styled markup — into a margin box. Both are for dynamic headers that change per section; static page numbers are already covered by PolyDoc templates.
/* Option A: text-only running string */
h1 { string-set: chapter content(); }
@page { @top-center { content: string(chapter); font-size: 9pt; } }

/* Option B: pull a full element (e.g. with a logo) into the header */
.running-header { position: running(header); }
@page { @top-center { content: element(header); } }

Cross-references

target-counter() resolves to the page number of the linked element. Use it to build tables of contents or "see page X" references that always stay in sync with the document layout.
.toc a::after {
content: leader(".") " " target-counter(attr(href url), page);
}

Facing pages

:left and :right pseudo-classes give even and odd pages different margins, which is essential for double-sided printing where the spine side needs extra space. Use case: books, reports, legal documents intended for binding.
@page :left { margin-left: 2.5cm; margin-right: 1.5cm; }
@page :right { margin-left: 1.5cm; margin-right: 2.5cm; }

Advanced layout

Vivliostyle supports CSS multi-column layout across paginated output, and vertical writing modes for CJK typesetting — features that go beyond what Paged.js currently covers. Use case: magazines, academic journals, Japanese or Chinese publications.
/* Magazine-style two-column body text */
.article-body {
column-count: 2;
column-gap: 1.5cm;
}

/* Vertical writing (Japanese, Chinese) */
body { writing-mode: vertical-rl; }
@page { size: A5; }

Paged.js vs Vivliostyle

Both libraries cover the same core CSS Paged Media features. The main differences are setup complexity, DOM impact, and a few specialised capabilities at each end.
AspectPaged.jsVivliostyle
IntegrationSingle script tag in your HTMLSeparate viewer page + hosted content
LicenseMITAGPL-3.0
DOM impactHeavy — rewrites entire documentIsolated viewer container
Bleeds & crop marksPartial
EPUB / vertical writing

Tips