Elements¶
An element is one piece of content on a page: a heading, a picture, a framed callout. Every element has a Type, which decides which other fields it understands.
Elements appear in five places, and where they appear changes how they're positioned:
| Where | Positioning |
|---|---|
Page.Elements |
Stacked top to bottom. |
Page.Background |
Placed by Position, drawn behind the page's elements. |
Page.Foreground |
Placed by Position, drawn over the page's elements. |
Book.Underlay |
Placed by Position relative to the book, drawn behind the book sprite. |
Book.Overlay |
Placed by Position relative to the book, drawn in front of everything. |
Where an element appears doesn't change what it can do. A tooltip, an Action or a HoverAction works the same in any of the five. Note that an element in Page.Background or Page.Foreground with none of those is transparent to the cursor, so decorative art doesn't cover the page.
Element types¶
-
Title (
Title)Large heading text.
-
Heading (
Heading)Section heading text.
-
Paragraph (
Paragraph)Body text.
-
Image (
Image)A sprite, an animation or an item's icon, optionally with text drawn on it.
-
Divider (
Divider)A horizontal rule, plain or decorative.
-
Panel (
Panel)A nine-sliced frame containing other elements.
-
Banner (
Banner)A three-sliced strip with text in the middle, a scroll or ribbon.
-
Button (
Button)A nine-sliced frame with a label, for running an action.
-
Page number (
PageNumber)The page's own number, filled in automatically.
-
Grid (
Grid)A container laying its children out across fixed-size cells.
-
Input (
Input)A text box the reader types into, for filtering a page against what they've typed.
An unrecognised Type is skipped with a warning rather than breaking the book.
Common fields¶
Every element understands these, whatever its type.
| Property | Type | Default | Description |
|---|---|---|---|
Type required |
element type |
— | Which kind of element this is. Determines every other field below. |
Id optional |
string |
— | An optional identifier for this element. Not used for navigation, purely for your own reference. |
Alignment optional |
Left | Center | Right |
Left |
Where the element sits within its container's width. Only has an effect when the element is narrower than the space available (see Layout). In a placed list it anchors the element and Position is measured from there. |
VerticalAlignment optional |
Top | Center | Bottom |
Top |
Where the element sits within its container's height. Only used in Background, Foreground, Underlay and Overlay, since a stacked element takes its vertical position from the elements above it (see Placed elements). Setting it on a stacked element logs an error and does nothing. |
Scale optional |
number |
1 |
The element's scale. Its meaning depends on the element type: sprite scale for Image, Panel, Banner, Button and Divider. Font scale for Title, Heading and Paragraph. See Units and scale. |
SpacingAfter optional |
int |
8 |
The gap between this element and the next one, in unscaled sprite pixels × Scale. Not applied after the last visible element, so a trailing gap can't appear at the bottom of a page or panel. |
MarginLeft optional |
int |
0 |
Space reserved to the element's left, in unscaled sprite pixels × Scale. This narrows the width the element measures against, so text wraps at the indented width rather than overflowing. |
MarginRight optional |
int |
0 |
Space reserved to the element's right, in unscaled sprite pixels × Scale. |
Position optional |
Point |
{ X: 0, Y: 0 } |
The element's position, in screen pixels. Only used in Background, Foreground, Underlay and Overlay, where elements are placed rather than stacked. Measured from wherever Alignment and VerticalAlignment anchor the element, so under the default Left and Top it's a plain coordinate (see Placed elements). Unlike every other spacing field this is not multiplied by Scale. Changing an element's scale resizes it in place rather than moving it. |
Condition optional |
string |
— | A game state query. When it evaluates false the element is hidden, and elements below it close the gap. Re-checked several times a second while the book is open. Understands tokens. |
Lifetime optional |
number |
— | Seconds the element stays up once shown. Setting it makes the element a timed one: hidden until a ShowElement action names it, so an Id is required. |
FadeAfter optional |
number |
— | Seconds before it starts fading, reaching nothing at the end of Lifetime. Needs Lifetime, and must be below it. |
IgnoreCursor optional |
bool |
false |
Lets the cursor pass straight through the element to whatever sits beneath it, so it can't be hovered or clicked. Its children and its own Background and Foreground are still reachable (see Passing the cursor through). |
Action optional |
string |
— | A trigger action run when the element is clicked. When set, the element becomes interactive: any element type can have one, not just Button. Understands tokens. |
Actions optional |
list of string |
— | Trigger actions run in order when the element is clicked. Combined with Action rather than replacing it, so an element with both runs Action first. Understands tokens. |
Sound optional |
string |
bigSelect |
The cue played when the element is clicked. Only used when Action or Actions is set, and played once however many actions run. Set to null for a silent click. |
HoverAction optional |
string |
— | A trigger action run when the cursor moves onto the element. Runs on entry, not while the cursor rests there. Sound doesn't apply. Understands tokens. |
HoverActions optional |
list of string |
— | Trigger actions run in order when the cursor moves onto the element. Combined with HoverAction rather than replacing it, so an element with both runs HoverAction first. Understands tokens. |
DisplayName optional |
string |
— | The bold title of the element's hover tooltip. Works in every list, including a page's Background and Foreground. Can carry tokens. |
Description optional |
string |
— | The body of the element's hover tooltip. Works in every list, including a page's Background and Foreground, where setting it is also what makes an otherwise decorative element reachable by the cursor. Ignored when IgnoreCursor is set, which logs a warning. Can carry tokens. |
Tags optional |
list of string |
— | Free-form markers other mods can read off whatever the cursor is over, such as Lookup Anything support. Tagging an element also makes it reachable by the cursor, since a tag is only useful on something that can be hovered. Ignored when IgnoreCursor is set. See Tags. |
ParseTokenizableStrings optional |
bool |
true |
Whether the game's [Token] tokenizable strings are resolved in this element's Text, DisplayName, Description, Condition and actions. Set it to false where a square bracket is meant as the character itself. |
Any element can be clickable
Action lives on every element, not just Button. An Image with an Action is a perfectly good bookmark or tab. Button is just the shorthand for the common case of a framed label.
Text fields¶
Understood by Title, Heading, Paragraph, Banner, Button, Image and Input.
Any element's Text can carry tokens, placeholders replaced with something the book knows as the element is laid out. That covers Parchment's own %Token% forms and the game's [Token] tokenizable strings.
| Property | Type | Default | Description |
|---|---|---|---|
Text optional |
string |
— | The text to draw. Wraps automatically to the available width, including breaking words that are too long for a line on their own. \n forces a line break. Can carry tokens, both Parchment's %Token% forms and the game's tokenizable strings. |
FontType optional |
font type |
varies | Which font to draw with. The default differs per element type. |
TextColor optional |
color |
the book's default | The text color. |
ShadowColor optional |
color |
the game's shadow color | The color of the drop shadow drawn behind the text, alpha included. Left off, the shadow follows TextColor's alpha instead. Ignored when FontType is SpriteText, which draws its own outline. |
Sprite fields¶
Understood by Image, Panel, Grid, Banner, Button, Divider and Input.
| Property | Type | Default | Description |
|---|---|---|---|
TexturePath optional |
string |
— | The asset name of the sprite sheet, such as Assets/PeacefulEnd.Parchment/panelFrame2 or a vanilla path like LooseSprites/Cursors. This is a game asset name, not a file path in your content pack. Load your PNG into the game's content with a Content Patcher Load patch first. See Loading your art. |
TextureSourceRectangle optional |
Rectangle |
the whole texture | The area of the sprite sheet to draw. Almost always needed: without it, the whole sheet is used, which for a multi-sprite sheet means a much larger element than you expect. |
HoverTextureSourceRectangle optional |
Rectangle? |
— | An alternate source rectangle drawn while the cursor is over the element. Must be the same size as TextureSourceRectangle. The element's layout is measured from the normal rectangle, so a differently-sized hover sprite will overhang its own bounds. For a moving hover state on an Image, use HoverFrames instead. |
TintColor optional |
color |
white | A color multiplied into the sprite. Multiplying means it can only darken: red on grey art gives red, red on blue art gives near-black. Best on neutral or greyscale art. |
SpriteEffects optional |
None | FlipHorizontally | FlipVertically |
None |
Mirrors the sprite within its own bounds. It does not move the element. If flipping appears to shift the art sideways, the sprite has uneven transparent padding inside its source rectangle. Combine with a comma: "FlipHorizontally, FlipVertically". Ignored by Panel and Button, which are nine-sliced. |
Animation fields¶
Understood by every element type. On an Image a frame steps through a sprite sheet. Everywhere else it moves the element, times a trigger action, or both.
| Property | Type | Default | Description |
|---|---|---|---|
Frames optional |
list of frames |
— | Animation frames. When omitted, the element draws where it was laid out. |
HoverFrames optional |
list of frames |
— | Animation frames played while the cursor is over the element, replacing Frames for as long as it stays there. See Hover frames. |
FrameDuration optional |
number |
100 |
How long a frame is shown when it doesn't specify its own Duration, in milliseconds. |
Frame fields¶
Each entry in Frames or HoverFrames:
| Property | Type | Default | Description |
|---|---|---|---|
Offset optional |
Point |
{ X: 0, Y: 0 } |
How far this frame is shifted from where the element sits, in unscaled sprite pixels × the element's Scale. Positive moves right and down. See Frame offset. |
Duration optional |
number |
the element's FrameDuration |
How long this frame is shown in milliseconds. |
Condition optional |
string |
— | A game state query deciding whether this frame plays. When omitted the frame always plays. Understands tokens. |
Action optional |
string |
— | A trigger action run each time this frame starts. Understands tokens. See Frame actions. |
Actions optional |
list of string |
— | Trigger actions run in order each time this frame starts. Combined with Action rather than replacing it. Understands tokens. |
SourcePoint optional |
Point |
the element's own sprite | Sprite elements only. The coordinate of the art for this frame, inheriting the element's TextureSourceRectangle for width and height. |
Scale optional |
number |
1 |
Image only. A multiplier on the element's Scale while this frame draws. See Frame scale. |
SourcePoint works on anything that draws art, so a Panel, Button, Banner, Divider, Grid or Input can step through a sheet the same way an Image does. It only moves where the art is read from, never how much of it, so a nine-sliced element keeps the border and the inset it was measured with.
Scale is the one field an Image alone can apply, since it's the only element drawn as a single quad with nothing inside it sized against it. Setting either field where it doesn't apply fails validation with a message saying so, rather than being quietly ignored.
Frames loop, and the cycle is timed from the moment the animation starts, so the first frame is the one that draws when it does.
A frame whose Condition fails is skipped, not paused on. The cycle gets shorter and the remaining frames close the gap, the same way a hidden element lets the ones below it close up. Conditions are re-checked while the book is open, so an animation can gain and lose frames as the game state changes.
Gaining or losing a frame starts the animation over. A cycle whose frame list changed isn't the cycle that was playing, so it restarts rather than resuming partway. That's what lets an animation gated behind a condition play properly: gate every frame on PeacefulEnd.Parchment_CurrentPageId <your page> and the whole thing plays from the top when the reader arrives, instead of catching it mid-cycle.
Timing a pause into a loop
Since the animation restarts when it becomes active, a long final frame reads as a delay before the next repeat. A ten-frame flourish followed by a frame of 60000 plays once on arrival then holds still for a minute, over and over, without needing anything to trigger it.
Animating a panel's frame¶
SourcePoint swaps the art without touching the layout, so a nine-sliced border can flicker, pulse or react to the game state. Every frame has to point at a patch laid out the same way and the same size, since the element was measured once against those dimensions:
{
"Type": "Panel",
"TexturePath": "{{ModId}}/frames",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 18, "Height": 18 },
"Frames": [
{ "Duration": 400 },
{ "Duration": 400, "SourcePoint": { "X": 18, "Y": 0 } }
]
}
Nothing checks that the art fits
A source point aimed at a patch that isn't nine-sliceable, or that has a different border, draws a mangled box rather than raising an error. There's no way to tell intent apart from a mistake here, so the sheet is yours to lay out carefully.
Moving an element¶
Offset is the field that works on every element type, sprite or not. The element is measured once and keeps that space and that hitbox, so it slides over its own footprint rather than pushing the page around:
{
"Type": "Panel",
"TexturePath": "{{ModId}}/frame",
"TextureSourceRectangle": { "X": 0, "Y": 0, "Width": 18, "Height": 18 },
"Frames": [
{ "Duration": 700 },
{ "Duration": 700, "Offset": { "X": 0, "Y": -1 } }
],
"Children": [
{ "Type": "Paragraph", "Text": "Everything in here comes along for the ride." }
]
}
A container carries its children and its own Background and Foreground with it, since they are all drawn relative to the rectangle it was given.
Offsets add up
A moving element inside a moving container is shifted by both. Animate the container or its contents, not usually both, or pick offsets that read well together.
Font types¶
| Value | What it is |
|---|---|
Dialogue |
The game's main dialogue font. Large. |
Small |
The game's small font. The usual choice for body text and labels. |
Tiny |
The game's tiny font. |
SpriteText |
The game's bitmap title font, the one vanilla uses for menu headers. Its natural size is large: a dozen characters at TextScale: 1 is around 300 pixels wide. |
TextScale: 1 means each font's own natural size, so switching FontType changes the size. On elements that have both a sprite and text (Banner, Button and a text-bearing Image) TextScale sizes the text and Scale sizes the sprite, independently.
Sizing modes¶
Used by Panel, Banner, Button and Divider to decide how wide they are.
| Value | Behaviour |
|---|---|
Fill |
Take the full width available. |
ShrinkToFit |
Be exactly as wide as the contents need. The element is then placed by its Alignment. |
Fixed |
Be exactly Width wide. Requires Width. |
In every mode the result is clamped to the space available, so an element can never be wider than its container.
Colors¶
Color fields accept any of:
| Form | Example |
|---|---|
| A color name | "SkyBlue" |
| RGB hex | "#8B4513" |
| RGBA hex | "#8B4513FF" |
| 8-bit RGB | "34 139 34" |
| 8-bit RGBA | "34 139 34 255" |
Values are space-separated, not comma-separated. An unparsable color logs a warning and falls back to the default.
Alpha is optional and full strength when left off. Write the color you want at full strength and let the alpha fade it, the same way the game does elsewhere: "255 0 0 128" is a half-faded red rather than a brighter one. The channels are scaled by the alpha before anything is drawn, so a translucent color fades towards whatever is behind it instead of washing out towards white.
Rectangles and points¶
Rectangles and points are objects: