BusinessTheme bundles a DocumentPalette, SpacingScale,
TextScale, TablePreset, and an optional page background, so
invoice / proposal / report templates rendered through the same
theme look like one product instead of three independently styled
documents.
Three built-ins ship with the canonical surface:
import com.demcha.compose.document.theme.BusinessTheme;
BusinessTheme classic = BusinessTheme.classic(); // crisp blue + white
BusinessTheme modern = BusinessTheme.modern(); // cream paper + teal/gold
BusinessTheme executive = BusinessTheme.executive(); // graphite + warm accentEach theme exposes a palette, a text scale, a table preset, a spacing scale, and an optional page background:
document.pageFlow(page -> page
.addSection("Hero", section -> section
.softPanel(theme.palette().surfaceMuted(), 10, 14)
.accentLeft(theme.palette().accent(), 4)
.addParagraph(p -> p
.text("GraphCompose")
.textStyle(theme.text().h1()))
.addParagraph(p -> p
.text("A theme-driven hero section.")
.textStyle(theme.text().body()))));BusinessTheme.modern() ships with a cream pageBackground(). Apply
it on the document builder so the entire page paints with the
theme's paper colour rather than pure white:
try (DocumentSession document = GraphCompose.document(Path.of("output.pdf"))
.pageBackground(theme.pageBackground())
.margin(28, 28, 28, 28)
.create()) {
// ...
document.buildPdf();
}Reach for the named slots on theme.palette() instead of hard-coding
hex values — this keeps the document consistent with the rest of the
theme:
| Slot | Typical use |
|---|---|
palette().accent() |
Brand accent on borders, badges, total rows |
palette().surfaceMuted() |
Soft section backgrounds (softPanel(...)) |
palette().textPrimary() |
Body copy |
palette().textMuted() |
Captions, metadata |
The exact RGB values are an implementation detail of each theme — the slot names stay stable across future tweaks.
theme.text() returns a TextScale with named slots (h1, h2,
bodyBold, body, caption). Use them instead of hand-rolling
DocumentTextStyle.builder()... calls so your headings stay
consistent.
.addParagraph(p -> p.text("Quarterly summary").textStyle(theme.text().h2()))
.addParagraph(p -> p.text("Generated " + date).textStyle(theme.text().caption()))InvoiceTemplateV2 and ProposalTemplateV2 (Phase E.1 / E.2) take a
BusinessTheme in their constructor:
import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
import com.demcha.compose.document.theme.BusinessTheme;
InvoiceTemplateV2 invoice = new InvoiceTemplateV2(BusinessTheme.modern());
ProposalTemplateV2 proposal = new ProposalTemplateV2(BusinessTheme.modern());The same business data (InvoiceDocumentSpec,
ProposalDocumentSpec) renders in any of the three built-in themes
just by passing the theme to the constructor — no need to refactor
the call sites.
The earlier InvoiceTemplateV1 is hard-coded to a default theme via
the static BusinessDocumentSceneStyles. Both V1 and V2 ship side by
side; V2 is the cinematic theme-driven path.
CV templates are themed independently of BusinessTheme. The layered
CV presets (cv.v2.presets.*) carry their own theme type, CvTheme
(in com.demcha.compose.document.templates.cv.v2.theme), so CV tokens
stay separate from invoice / proposal vocabulary. Each preset ships a
default theme; render one against a CvDocument with its create()
factory:
import com.demcha.compose.document.templates.api.DocumentTemplate;
import com.demcha.compose.document.templates.cv.v2.data.CvDocument;
import com.demcha.compose.document.templates.cv.v2.presets.ModernProfessional;
DocumentTemplate<CvDocument> cv = ModernProfessional.create();
cv.compose(session, cvDocument);To restyle, pass a custom CvTheme to the preset's create(...)
overload, or use its Options builder where one is provided (for
example MintEditorial.Options.builder().headerBandColor(...).build()
→ MintEditorial.create(options)). See
authoring presets for
the per-preset customisation knobs and
which template system? for the
full preset list.
- Shape-as-container — themed circles + cards.
- Tables — the table style overrides used by
headerStyle(...),totalRow(...), andzebra(...)accept fullDocumentTableStylevalues, so they pick up theme palettes. docs/architecture/canonical-legacy-parity.md— the parity matrix lists every theme-aware style override that v1.5 supports.