FolioTier 1

theme-tokens

Every value the renderer reads when laying out a project — dialogue box, namebox, choice buttons, colors, type, playback, textbox, reference resolution.

Reference page — theme tokens are a typed snapshot the renderer reads, not a verb the parser emits. The tokens are real today; the Phase 2 JSON5 authoring surface is in flight.

What a theme is

A project carries one presentation snapshot — a typed value with two halves: tokens (numeric / color / font configuration) and assets (references to images and fonts in the project's asset library). The renderer reads the snapshot on every frame; the authoring surface in Phase 2 is a JSON5 editor over the same shape.

The canonical shape is defined in canonical-project.ts (PresentationTokens + PresentationAssets); the two seed values live in presentation-presets.ts (renpyDefaultPreset(), modernPreset()). Importing a Ren'Py project produces a renpy-default snapshot derived from the project's gui.rpy; new Vizno projects start on modern.

Coordinate system

All x/y/width/height values are in reference-resolution pixels. Reference resolution defaults to 1280 × 720 (matching Ren'Py's default base); the renderer scales the whole stage to fit the player surface while preserving aspect ratio.

xalign / yalign are 0-to-1 anchor scalars (0 = left/top, 0.5 = center, 1 = right/bottom). xpos / ypos are absolute pixel offsets at reference resolution. Mixed positioning (anchor + offset) matches Ren'Py's xalign 0 xpos 268 semantics — anchor first, then offset from that anchor.

dialogue

Layout of the dialogue text inside the textbox.

| Token | Type | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---|---| | textXalign | number | 0 | 0 | Horizontal anchor of the text inside the textbox. 0 = left, 0.5 = center. | | width | number | 744 | 1024 | Wrap width in pixels. Long lines break at this width. | | xpos | number | 268 | 128 | Left margin from the stage's left edge. | | ypos | number | 50 | 28 | Top margin from the textbox's top edge (not the stage's). |

namebox

The character-name plate that sits at the top-left of the textbox.

| Token | Type | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---|---| | borders | {top,right,bottom,left} | 5/5/5/5 | 8/16/16/8 | Inner padding inside the namebox background. | | height | number | null | null | null | Fixed height in pixels; null lets the text drive it. | | tile | boolean | false | false | When true, tile the namebox background image instead of stretching. | | width | number | null | null | null | Fixed width in pixels; null lets the text drive it. | | xalign | number | 0 | 0 | Horizontal anchor of the namebox inside its parent. | | xpos | number | 240 | 0 | Horizontal offset from the anchor. | | ypos | number | 0 | -36 | Vertical offset from the textbox's top edge. Negative values float the namebox above the textbox. |

choice

The button column for choice blocks.

| Token | Type | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---|---| | button.borders | {top,right,bottom,left} | 5/100/100/5 | 12/24/24/12 | Inner padding inside each button. Ren'Py's wide left/right reflects sidebar art frames. | | button.height | number | null | null | null | Fixed button height; null lets the label drive it. | | button.hoverColor | hex string | #ffffff | #ffffff | Label color on hover. | | button.idleColor | hex string | #cccccc | #ffffff | Label color at rest. | | button.textXalign | number | 0.5 | 0.5 | Horizontal alignment of the label inside the button. | | button.tile | boolean | false | false | Tile the button background image instead of stretching. | | button.width | number | 790 | 720 | Button width in pixels. | | spacing | number | 22 | 14 | Vertical gap between consecutive buttons. | | xalign | number | 0.5 | 0.5 | Horizontal anchor of the column. | | yanchor | number | 0.5 | 0.5 | Vertical anchor of the column. | | ypos | number | 270 | 520 | Vertical position of the column at the anchor. |

colors

Project-wide color palette. Theme tokens that take a color (e.g. button.idleColor) resolve hex literals directly; in Phase 2 they will also accept token names from this group (colors.accent, colors.text).

| Token | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---| | accent | #cc6600 | #b5542f | Brand accent — selected choice, active link, system highlights. | | hover | #e0a366 | #e0a366 | Generic hover state where no specific color is set. | | hoverMuted | #7a3d00 | #7a3d00 | Hover state for muted / secondary elements. | | idle | #555555 | #9ca3af | Default text color for non-dialogue UI surfaces (menus, history). | | idleSmall | #aaaaaa | #cbd5f5 | Smaller / secondary text color. | | insensitive | #5555557f | #94a3b87f | Disabled state color. Includes alpha. | | interfaceText | #ffffff | #ffffff | Default text color inside system UI (preferences, save / load). | | muted | #512800 | #3a1e10 | Quiet background fill behind muted text. | | selected | #ffffff | #ffffff | Selected-state text color (e.g. current language). | | text | #ffffff | #ffffff | Default dialogue text color. |

type

Font family + size for the three text surfaces.

| Token | Type | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---|---| | textFont | string | DejaVuSans.ttf | Inter, system-ui, sans-serif | Dialogue font. Filename when bundled (e.g. DejaVuSans.ttf) or CSS family stack when web-only. | | textSize | number | 22 | 22 | Dialogue font size in points at reference resolution. | | nameTextFont | string | DejaVuSans.ttf | Inter, system-ui, sans-serif | Namebox font. | | nameTextSize | number | 30 | 16 | Namebox font size. | | interfaceTextFont | string | DejaVuSans.ttf | Inter, system-ui, sans-serif | System-UI font (preferences, save / load). | | interfaceTextSize | number | 24 | 18 | System-UI font size. |

playback

Runtime behaviors that affect how scenes play, not how they look.

| Token | Type | Default | Means | |---|---|---|---| | clickAnywhereToAdvance | boolean | true | When true, a click anywhere in the player advances the current line. When false, only the advance button does. | | textCps | number | 30 | Characters per second for the dialogue typing effect. 0 disables the effect (text appears whole). The {slow} text tag halves this; the {cps=…} tag is deferred (see text-tags). | | windowShowTime | number | 0.2 | Seconds the textbox takes to fade in when dialogue resumes from a window hide state. | | windowHideTime | number | 0.2 | Seconds the textbox takes to fade out when entering a window hide state. |

transition dissolve and transition fade now lower through the effect catalog (@dissolve / @fade); their default duration is 0.5s, fixed in transition-lowering.ts. Use @dissolve duration=… or @fade duration=… to override per-call.

textbox

The dialogue textbox container itself (background + position).

| Token | Type | Default (Ren'Py) | Default (modern) | Means | |---|---|---|---|---| | height | number | 185 | 232 | Textbox height in pixels at reference resolution. | | yalign | number | 1 | 1 | Vertical anchor of the textbox in the stage. 1 = pinned to the bottom edge. |

The textbox's background image is an asset reference (see § assets).

reference

Reference resolution for the entire stage. All positional tokens are measured against this; the renderer scales the stage to fit the player surface while preserving aspect ratio.

| Token | Type | Default | Means | |---|---|---|---| | width | number | 1280 | Reference stage width in pixels. | | height | number | 720 | Reference stage height in pixels. |

Vizno does not currently support per-project reference resolution overrides at runtime — the snapshot value is informational and used by the importer to translate Ren'Py's gui.init width / height.

assets

The presentation.assets map references asset IDs in the project's asset library (the same library show / scene / audio verbs reference). Each field is optional; missing fields fall back to the runtime's default rendering.

| Field | Means | |---|---| | textboxBackground | PNG used as the dialogue textbox background. Imported from gui/textbox.png. | | nameboxBackground | PNG used behind the namebox. Imported from gui/namebox.png. | | choiceButtonIdleBackground | PNG behind each choice button at rest. Imported from gui/button/choice_idle_background.png. | | choiceButtonHoverBackground | PNG behind each choice button on hover. Imported from gui/button/choice_hover_background.png. | | textFont | Font asset (TTF / OTF / WOFF / WOFF2) for dialogue. | | nameFont | Font asset for the namebox. | | interfaceFont | Font asset for system UI. |

Importer linking: when a project ships gui/textbox.png, the importer's deterministic pass writes the asset ID to presentation.assets.textboxBackground. Same for the other well-known Ren'Py paths.

Notes

The snapshot model means there is no layoutPreset runtime branch — old code that did if (layoutPreset === "modern") {...} was removed in Phase 2 Step 1, and the vestigial layoutPreset field was stripped from snapshots entirely in Step 6 (one-shot SQL migration plus type / validator edits). The two preset functions in presentation-presets.ts remain as seed-value helpers — createBlankCanonicalProject and the importer hydrate new projects from them, and the studio Theme tab's "apply preset" buttons drop a fresh preset into the editor buffer — but the renderer reads tokens, not preset names.

Per docs/folio-roadmap.md § Out of scope for v1: the visual theme editor (knobs and sliders for these tokens) is not v1 — JSON5 editing is the only authoring surface in v1.x.

See also

  • scene / show — verbs whose stage placement reads from these tokens
  • say / narrate — dialogue verbs whose textbox / namebox / type tokens this page documents
  • choice — choice button styling lives in the choice token group
  • text-tags — inline grammar that overrides colors.text / type.textSize per span