Skip to content

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.

ts
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

TokenResolves to
$colors.primary$colors.<any>Theme colours, including custom names you add.
$typography.headingFont, $typography.bodyFont, $typography.monoFontFont stacks.
$table.headerBackground, $table.headerColor, $table.zebraBackground, $table.borderColorTable defaults.
$logo, $logoDarkLogo 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 }).