Core concepts
The block model, the builder DSL, the theme system and the Renderer interface.
The document model
A Document has meta and a list of blocks. zod is the source of truth — every
type is inferred from a schema, and the same schemas validate API payloads.
interface Document {
meta: { title: string; author?: string; subject?: string };
blocks: Block[];
}The block union:
| Block | Shape |
|---|---|
heading | { text, level: 1 | 2 | 3 } |
paragraph | { text, emphasis? } |
table | see Tables |
keyValue | { items: { label, value, format? }[] } |
section | { title?, blocks: Block[], sheet? } |
chart | { chartType, data, x, y, title? } — table fallback in xlsx |
image | { src: Uint8Array | string, width?, height? } |
spacer | { size } |
pageBreak | {} |
The builder DSL
Build the tree with small helpers, then validate once at the boundary with
defineDocument, which throws a typed LiasseValidationError on bad input.
import { defineDocument, heading, paragraph, table, keyValue, kv, section } from "@liasse/core";
const doc = defineDocument({
meta: { title: "Report" },
blocks: [
heading("Summary", { level: 1 }),
paragraph("Quarterly figures by line item."),
keyValue([kv("MRR", 622350, "currency"), kv("Accounts", 1284, "number")]),
],
});Tables
table is the workhorse:
table({
columns: [
{ key: "item", header: "Item" },
{ key: "ca", header: "Revenue", format: "currency", align: "right" },
],
rows: data,
summary: { ca: "sum" }, // sum | avg | count
conditional: { column: "ca", rule: "positiveNegative" },
xlsx: { freezeHeader: true, sheetName: "Detail" }, // format-specific escape hatch
});formatper column:text,number,currency,percent,date.summaryemits a real Excel formula row (=SUM,=AVERAGE,=COUNTA).conditional: positiveNegativecolors cells using the theme's semantic tokens.xlsxis a typed escape hatch: hints read only by the xlsx adapter, so the core stays format-agnostic.
Sections & sheets
A section with sheet: true becomes its own worksheet; otherwise its blocks
flow into the current sheet.
section({ title: "Detail", sheet: true, blocks: [ table({ /* … */ }) ] });The theme
A theme is a plain, JSON-serializable object. Ship defaultTheme or derive one
with defineTheme:
interface Theme {
palette: {
primary: string; accent: string;
neutral: { fg: string; muted: string; border: string; bg: string };
semantic: { positive: string; negative: string };
};
typography: { fontFamily: string; baseSize: number; headingSizes: [number, number, number] };
table: { headerFill: string; headerFg: string; banding: boolean; borderColor: string };
spacing: number;
brand?: { logo?: Uint8Array; headerText?: string; footerText?: string };
}The adapter translates each token into an exceljs style primitive (a color
token → a fill/font.color, etc.). Components never hard-code styling.
Rendering
The Renderer interface is tiny:
interface Renderer {
format: "xlsx" | "pptx" | "docx" | "pdf";
render(doc: Document, theme: Theme): Promise<Uint8Array>;
}@liasse/core exposes a thin render(doc, { format, theme? }) that dispatches to
a registered renderer. Importing an adapter registers it:
import { render } from "@liasse/core";
import "@liasse/xlsx"; // registers the "xlsx" renderer
import "@liasse/pptx"; // registers the "pptx" renderer
import "@liasse/docx"; // registers the "docx" renderer
import "@liasse/pdf"; // registers the "pdf" renderer
const workbook = await render(doc, { format: "xlsx" });
const deck = await render(doc, { format: "pptx" });
const wordDoc = await render(doc, { format: "docx" });
const pdf = await render(doc, { format: "pdf" });@liasse/pptx(pptxgenjs) renders titled sections to slides with overflow handling and native PowerPoint charts.@liasse/docx(thedocxlibrary) renders a flowing Word document with real tables and an optional brand header/footer.@liasse/pdf(pdfkit) renders a flowing A4 PDF with paginated tables (the header repeats across pages). This is a general report PDF — Factur-X / PDF-A-3 conformance is a separate module on the roadmap.
In xlsx, exceljs native charts are incomplete, so a chart block renders
as a clean data-table fallback (and logs a one-time warning). In pptx,
charts are native.