Themes
A theme is one JSON file that says how a tenant's documents look. It carries fonts, colours, typography scales, page geometry, component styles (callouts, tables), and a chart palette. ScaiScribe compiles each theme into format-specific artefacts — a Word styles.xml, a PPTX masters config, an XLSX styles block, an ECharts theme JSON — and uses those compiled forms when rendering.
A tenant pins their documents to a theme by name. Change the theme; re-render; the document picks up the new look without any spec change.
Theme structure#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
Format-specific compilation#
A single theme produces five compiled artefacts. Each renderer consumes the one it needs:
| Compiled form | Consumed by | Notes |
|---|---|---|
docx_styles (XML fragment) |
render-docx.js | Injected via dolanmiu/docx's externalStyles. Carries Normal, Heading1–4, CalloutInfo/Warn/Note, CodeBlock, PageBreak. |
pptx_master (JSON config) |
render-pptx.js | Drives pres.defineSlideMaster calls + slide colours/fonts + per-master layout zones (title/subtitle/body rectangles). |
xlsx_styles (JSON config) |
render-xlsx.js | Carries the header_row/alternating_row/totals_row cell blocks + the number_formats catalogue. |
html_css (CSS bytes) |
render-html-preview.js | Wraps the mammoth-emitted HTML for DOCX preview. |
echarts (JSON theme) |
render-chart-image.js | Theme palette for the SVG chart fallback. |
Compilation is cached by content hash. The same theme, compiled twice, hits the cache the second time — so theme changes propagate but don't re-cost the CPU.
Inheritance via extends#
A theme can inherit from a parent:
1 2 3 4 5 | |
The child overrides what it specifies; everything else is inherited from the parent. v1.0 supports single-level extension (no chains). Override semantics are per-property: setting colors.primary doesn't blow away the rest of colors; it merges in.
Slide layout zones#
PPTX masters declare per-layout zones — rectangles for title, subtitle, and body content. The renderer uses these to place slide elements: title and subtitle land in their fixed slots, and body-shaped elements (paragraph, bullet_list, image, chart) stack sequentially inside the body zone with heights proportional to estimated content weight. This is what stops the classic "paragraph plus bullet list on the same slide render on top of each other" problem.
Themes that omit zones fall back to the renderer's defaults (sized for a 13.33″ × 7.5″ LAYOUT_WIDE canvas).
Tenant fonts#
Themes can reference fonts by family name. If the tenant has uploaded the matching face via POST /v1/fonts, the renderer picks it up automatically — font upload extracts family/weight/style via FontTools and indexes the binary for the workers. If no tenant-uploaded face matches, the renderer falls back to the theme's fonts.fallbacks array.
For PDF specifically, embedded fonts get baked into the output via Gotenberg + LibreOffice.
How themes apply by format#
- DOCX: theme styles become Word styles (
Heading1,CalloutInfo,CodeBlock). Authors writing{"type":"heading","level":1,...}getHeading1automatically. - PPTX: each
layoutin a slide references a master id frompptx.masters[]. Render-pptx callspres.addSlide({masterName: layout})and lays elements out using the master'szones. - XLSX: cells with
style_idget the matchingxlsx.{header_row,alternating_row,totals_row}block applied. Numbers withnumber_formatuse the theme's format string.
Built-in themes#
| Theme | Use case |
|---|---|
office |
Default. Aptos with Calibri fallback and a neutral palette — the same look Microsoft Office 2024+ ships with. Right for almost every request; the theme every caller gets when no theme is passed. |
minimal |
Helvetica, monochrome. Good baseline for white-label resellers. |
acme_variant |
Example of extends — inherits from office, overrides primary colour. Intended as a sample for tenants building their own brand theme. |
Tenant-owned themes#
Brand themes live on tenants, not in the builtin bundle. A tenant registers its theme via the admin path and gets a scoped theme_id addressable in any spec created under that tenant. Cross-tenant theme references are refused — a caller asking for a brand theme that isn't theirs gets 404 theme not found.
scribe_list_themes returns the visible set for the caller's tenant (builtins plus their own tenant themes, each stamped scope: "builtin" or scope: "tenant").
Where to next#
- Templates — themes vs templates (themes are styling, templates are pre-filled binary skeletons).
- Spec format — how a spec references a theme.
- Charts — chart_palette colours flow into native + image rendering.