Settings

Standard CSS carries most of a document’s appearance. What it can’t express — how content flows (pages vs scroll), where the app’s panes sit, what page furniture prints, what the editor does — rides the same stylesheet as namespaced custom properties. They cascade, condition, and layer exactly like every other declaration (put one inside @media print, inside a guard — it just works), and a bare browser simply ignores them.

Declare them on the document surface (main), or under any condition:

main { --spub-flow: continuous; --spub-outline: right; }
@media print { main { --spub-flow: paged; --spub-numbering: on; --spub-numbering-format: "{n}."; } }
html[data-spub-intent~="edit"] main { --spub-editor-current-line: highlight; }

Renderers resolve them through the engine’s cascade (resolveSettings) — the same result the browser would compute — and read them as one typed Settings object. Unknown values degrade to the default (warn, never fail).

The vocabulary

Pagination and flow — whether one-column content scrolls continuously or is split into pages. Multiple columns use a viewport-height horizontal flow:

Property Values (default first)
--spub-layout-justify-content space-evenly · start · center · end · space-between · space-around · stretch — placement of the canvas and visible in-layout Outline columns in the workspace
--spub-flow continuous · paged — UI labels: Multi column only · Always
--spub-flow-columns auto · 1 · 2 · 3 · 4 · 5 · 6 — text columns per viewport (auto fits as many as the reading width allows)
--spub-column-width length in rem · px · pt · em · % · words, default 34rem — preferred text-column width; words uses an average five-character word
--spub-grid-gap length, default 2rem — the gap between text columns
--spub-justify-content space-evenly · start · center · end · space-between · space-around · stretch — placement of the packed text column or columns in the content grid track
--spub-canvas-margin length in rem · px · pt · em · %, default 3rem — equal horizontal margins for the canvas area
--spub-outline-justify-content stretch · start · center · end — how Outline content aligns in its grid track when it participates in layout
--spub-scroll-direction vertical · horizontal
--spub-scroll-snap auto · none · page

The workspace is a CSS grid whose content and visible in-layout Outline occupy separate, gapless tracks. In multi-column reading the canvas track stretches to the workspace edges, resolving outer layout justification to stretch for that flow. --spub-canvas-margin becomes padding inside the scrolling canvas, so the initial and snapped views retain their inset while text travels to the viewport edge (or beneath an overlay Outline) during horizontal scrolling. --spub-layout-justify-content distributes the outer canvas and Outline tracks; non-stretch modes pack those tracks to their requested widths before distributing the remaining workspace. Column width, count, gap, and justification apply inside the content track in both continuous and paged reading. auto chooses the greatest whole number of preferred-width columns (up to the supported maximum) that fit inside the content track, including their gaps; the packed column block is then placed by --spub-justify-content. With multiple columns, space-between, space-around, and space-evenly distribute the content track’s remaining inline space between and, where applicable, around the columns; stretch expands the columns across that track. Once auto is resolved or an explicit count is selected, that count is authoritative; columns may shrink below their preferred width rather than collapsing to fewer columns. auto for snap resolves to page snapping for paged one-column and multi-column flows, and to none for continuous one-column flow.

Pagination is resolved after the column count. With one resolved column, paged (Always) uses vertical pages and continuous (Multi column only) leaves the document continuous. With two or more resolved columns, both values use native CSS multi-column fragmentation in a viewport-height container: the browser fills each column from top to bottom and continues the document horizontally. Scrolling snaps to each viewport-sized group of columns by default. CSS columns are anonymous boxes, so the renderer derives only the horizontal group step from the CSS container width and gap; it never measures text, lines, or content height. This avoids measured line slices and prevents content from extending below the viewport or being clipped at an inferred page boundary. The used horizontal column gap is never smaller than the padded inset, which keeps neighboring fragments outside that inset at snap points. The resolved count is recomputed when workspace or Outline geometry changes. The reading measure, padding, and margins are real CSS on main (max-width, padding, margin-inline).

Navbar — the outline / index navigator beside the document:

Property Values
--spub-outline right · left · top · bottom · overlay · hidden — where the navbar sits (hidden removes it)
--spub-outline-min-size / --spub-outline-max-size legacy range lengths
--spub-outline-width auto or length — preferred Outline width; auto uses its intrinsic content width capped by --spub-column-width
--spub-outline-display layout · overlay · spline — the top-menu Outline button toggles every mode; spline shows division numbers or dots until expanded
--spub-outline-font-size / --spub-outline-font-family outline typography; unset / inherit follows the script
--spub-outline-text-align start · center · end
--spub-outline-vertical-align top · middle · bottom
--spub-outline-indexes show · fold · hide — the derived index groups (Characters, Locations, …)

Source — the raw document source beside its continuous reading preview:

Property Values / meaning
--spub-source right · left — which side holds the Source panel
--spub-source-width length (default 24em) — Source panel width; the workspace divider adjusts it in whole-em steps
--spub-source-syntax-color on · off — color recognized syntax tokens
--spub-source-line-numbers on · off — show a line-number gutter
--spub-source-wrap-lines on · off — wrap long lines instead of scrolling horizontally
--spub-source-max-line-width none or length — cap the readable width of each source line

Source is a workspace scope, not a modal or a second document representation. The canvas renders the parsed work exactly as Read does, but resolves to one continuous scroll column while the source track shows the original text. Activating Source replaces any visible Outline and resolves the outer workspace distribution to stretch. The source track reaches both the workspace edge and the canvas seam; padding belongs inside the panel. Syntax coloring is a viewer aid and does not alter source bytes. HTML-aware coloring recognizes embedded JSON and CSS regions as their respective languages.

Progress — the reading-progress indicators:

Property Values
--spub-progress none, or a space list of bar · dots · percent · pages · divisions
--spub-progress-position bottom · top

Page furniture — templates take {n} {pages} {title} {author} {division}:

Property Values
--spub-numbering off · on
--spub-numbering-position bottom-center · bottom-left · bottom-right · top-center · top-left · top-right
--spub-numbering-format template string, default "{n}"
--spub-numbering-from integer (earlier pages unnumbered)
--spub-page-header / --spub-page-footer / --spub-page-watermark template strings
--spub-page-gutter / --spub-page-bleed lengths (beyond the native @page size/margins)
--spub-show-nonprinting hide · show — render blocks with behavior.printing: false (notes, boneyard) in paged / print output

Hiding a whole section is real CSS: section.notes { display: none; }.

Editor behaviour — ignored by read-only renderers:

Property Values
--spub-editor-kind-labels off · gutter · active
--spub-editor-placeholders on · off
--spub-editor-current-line off · highlight · typewriter
--spub-editor-focus off · paragraph · division
--spub-editor-line-numbers off · on
--spub-editor-input full · locked · comments-only
--spub-ribbon floating · top · bottom · hidden — the editor toolbar’s placement

Graceful degradation

A renderer that can’t page falls back to continuous flow and honours the rest. Settings a surface has no use for (editor knobs in a reader, panes in an EPUB) are simply unread. The machine-readable catalog — names, types, enums, defaults — is SETTINGS in @script-pub/format (sheet/settings.ts), the single source for the format editor’s forms and validation’s warnings.