Themes & page setup
A Theme is everything layout needs besides the document and the measurer: page geometry, typography and block styles. It is plain data, in CSS px.
The theme
import { defaultTheme, mergeTheme, mm, pt } from '@nextgensoftwares/folio-layout';
const theme = mergeTheme({
page: { width: mm(148), height: mm(210), margin: { top: mm(18), right: mm(15), bottom: mm(18), left: mm(15) } },
direction: 'rtl',
fonts: { body: 'Noto Sans Arabic, Geist', mono: 'Geist Mono' },
body: { size: pt(12), lineHeight: 1.7 },
headings: { 1: { size: pt(24), color: '#0b6266' } },
widows: 2,
orphans: 2,
});mergeTheme(overrides, base = defaultTheme) deep-merges a DeepPartial<Theme>. pt() and mm() convert to px.
| Section | Fields |
|---|---|
page | width, height, margin { top, right, bottom, left }, headerHeight, footerHeight |
direction | 'ltr' or 'rtl' (default direction of blocks without dir) |
fonts | body, mono (CSS family lists) |
body | size, lineHeight, color, paragraphSpacing |
headings[1..6] | size, weight, lineHeight, marginTop, marginBottom, optional color, family, italic, keepWithNext |
headingsKeepWithNext | Word-style: headings stay with the next block (default true) |
widows, orphans | minimum lines at a page top / bottom (default 2 / 2) |
list | indent, marginY, markerGap |
blockquote | indent, borderWidth, borderColor, marginY |
code | size, lineHeight, padding, marginY, radius, background, border, color, headerHeight, headerBackground |
table | cellPadX, cellPadY, border, headerBackground, marginY |
rule, math, media | spacing (and rule.color, rule.thickness) |
chrome | header/footer text size and color |
defaultTheme mirrors a canonical book stylesheet: A4 at 96 DPI (794 × 1123 px), 48 px margins, Geist + Noto Sans Arabic at 11 pt / 1.6.
Theme identity matters
Layout compares the theme by identity to decide whether cached flows and old pages are still valid. Create a theme once and keep it; a new theme object (even an equal one) re-lays out the document. That is correct after a real change, and wasteful on every render.
parseLength(value, em, percentOf) is the CSS-ish length parser used for node attributes: numbers are px; px, pt, em, rem and % are supported, and unitless strings are multiples of em (as for line-height).
Page setup UI
@nextgensoftwares/folio-react ships Word-like page setup components that edit theme.page and nothing else:
import { PageSetupPanel, HorizontalRuler, VerticalRuler } from '@nextgensoftwares/folio-react';
import { mergeTheme } from '@nextgensoftwares/folio-layout';
<PageSetupPanel
value={theme.page}
onChange={(page) => setTheme((t) => mergeTheme({ page }, t))}
unit="cm"
/>PageSetupPanel(and the modalPageSetupDialog, which edits a draft and applies once): paper size presets (A3, A4, A5, B5, Letter, Legal, Executive) or custom size, orientation (swapping sides rotates the margins like Word), margin presets (Normal, Narrow, Moderate, Wide) or custom margins, and header/footer band heights. Every value is validated bysanitizePageSetup(page withinPAGE_LIMITS, non-negative margins, at least 48 px of content each way).HorizontalRuler/VerticalRuler: MS Word-style rulers. Drag margins, and on the horizontal ruler drag the paragraph's first-line, hanging and left indent markers (written astextIndent/paddingInlineStart). Drags preview a guide line and apply once on release, because a page change re-lays out the whole book. Hold Alt to drag without snapping; RTL rulers count from the right.LengthInput: accepts2.5,2,5 cm,1in,72 pt; commits on Enter or blur, never per keystroke.
Units shown in the UI are mm, cm, in, pt or px; the engine always stores px (toPx, fromPx, parseLengthInput, formatLength).
Page look (display only)
PageLook (corner radius, shadow, border, margin guides) changes how pages look on screen. It is never part of the theme, never printed or exported, and never re-lays out the document. Use PageLookFields, pageLookStyle(look, zoom) and pageGuides(theme.page).
Theme editor
import { ThemeEditor } from '@nextgensoftwares/folio-react';
<ThemeEditor value={theme} onChange={setTheme} fonts={['Geist', 'Noto Sans Arabic']} />A controlled typography editor with tabs for presets, body text, headings H1–H6, blocks (lists, quotes, code, tables, rules) and JSON import/export, with an instant CSS preview (ThemePreview). Typed edits are debounced (commitDelay, default 400 ms) because each commit re-lays out the document; presets, imports and reset apply at once. It leaves value.page alone (except on import): page geometry belongs to the page setup panel.
Built-in presets (THEME_PRESETS): Classic book, Modern, Academic and Arabic academic, all within the Geist / Geist Mono / Noto Sans Arabic faces. Pass your own presets if you load more fonts.
Theme JSON
import { parseThemeJson, serializeTheme, validateTheme } from '@nextgensoftwares/folio-react';
const json = serializeTheme(theme, 'House style'); // { format: 'folio-theme', version: 1, … }
const { theme: imported, errors, warnings, fatal } = parseThemeJson(text, theme);validateTheme treats input as untrusted: every field is type- and range-checked, colours must be CSS colours, font families can't break out of a declaration, invalid fields keep the base value and unknown fields are reported. It never throws, and the returned theme is always complete and usable.