Themes
A theme is the brand: colours, typography, logo, font faces and free-form brand details. Templates reference tokens, so one saved template renders under any theme.
import type { Theme } from "@broadpaper/core";
export const meridian: Theme = {
id: "meridian",
name: "Meridian Wealth",
logo: "/brands/meridian.svg",
logoDark: "/brands/meridian-white.svg",
typography: { headingFont: "Inter, system-ui, sans-serif", bodyFont: "Inter, system-ui, sans-serif", baseFontSize: 11, headingWeight: 700 },
colors: {
primary: "#1F3A5F", secondary: "#4A6FA5", accent: "#E0B04B",
text: "#1A1D21", mutedText: "#6B7280", surface: "#FFFFFF", background: "#F5F6F8", border: "#E2E5EA",
success: "#1E8E5A", warning: "#C77D0A", danger: "#C0392B",
chart: ["#1F3A5F", "#4A6FA5", "#E0B04B", "#7FB3D5"]
},
fonts: [{ family: "Inter", src: "https://cdn.example.com/fonts/inter.woff2", weight: "400 700" }],
radius: 8,
table: { headerBackground: "#EEF2F7", headerColor: "#1F3A5F", zebraBackground: "#FAFBFC", borderColor: "#E2E5EA" },
meta: { company: "Meridian Wealth Management Ltd", regulator: "Authorised and regulated by the FCA (FRN 123456)" }
};Tokens
| Token | Resolves to |
|---|---|
$colors.primary … $colors.<any> | Theme colours, including custom names you add. |
$typography.headingFont, $typography.bodyFont, $typography.monoFont | Font stacks. |
$table.headerBackground, $table.headerColor, $table.zebraBackground, $table.borderColor | Table defaults. |
$logo, $logoDark | Logo URLs. The Logo block picks the dark variant on dark backgrounds automatically. |
In expressions, theme.meta.company and friends expose brand details for headers, footers and disclaimers. Missing values fall back to the built-in default theme and are reported as missing-theme-value warnings.
The cascade
Theme → document text styles → block defaults → instance overrides. New templates start with document styles that reference tokens (h1.color = $colors.primary), so they re-brand cleanly. Designers can still choose a literal colour for a block; hosts can forbid that with features.styleOverrides = false.
Fonts
Declare web fonts in theme.fonts; BroadPaper injects @font-face rules, waits for them to load before measuring, and re-measures if fonts arrive late. The PDF service loads the same declarations, so line breaks match. If your app loads fonts via <link>, that works too: the editor listens for font loading and re-lays out.
Switching at runtime
The toolbar's theme menu lists themes; designer.setTheme(theme) switches programmatically. For rendering, pass any theme to renderPdf({ template, data, theme }).