PTMLHomeGetting StartedReference

Language Reference

Complete reference for every PTML node, generated from the source schema definitions.
Declaration
breakpointsdefinefontsfunctionimportinitptmlrecordListstatetemplatevalueList
Block
boxbreakpointbuttoncellcheckboxcirclecompiledebugeachellipseformgroupheaderimageinputlabellinelinklistlistItemoptionpathpolygonpolylineradiorangerectrowselectshowsvgtabletexttextareawhen
Property
clickfontrecordrolestyleswhere
Conditional
elseif
Action
addRecordaddValuecallcleargetRecordgetValueremoveRecordremoveValuesetsetRecordsetValueupdateRecordupdateValuewindow

Declaration Nodes

Declaration nodes sit at the top level of a PTML file with no indentation and no prefix.
breakpointsDeclaration
Root node that defines named breakpoint labels and their pixel widths. Children are label: width pairs in ascending order; the last child has no width (and above tier). Each label names the range running up to its own width, so with small: 768, medium: 1024, large: the label small covers widths below 768 and large covers 1024 and up. Used by breakpoint blocks and define for responsive layout and conditional styles. A file uses its own declaration if it has one, otherwise it inherits the declaration of the first file it imports that has one.PREFIXnone (top-level)DATANot accepted -- this node takes no inline data.PROPERTIESAny property (Breakpoint label and optional width (pixels, no unit). Last child has no width.)EXAMPLE
breakpoints:
- small: 768
- medium: 1024
- large:
defineDeclaration
Root node for defining named CSS styles that can be referenced elsewhere. Style definitions can contain CSS properties (e.g., color, font-size) and conditional styles using if/else nodes. The style name is required and must be a valid identifier. A when child adds styles that apply only in an interaction state -- hover, focus, active, disabled or placeholder -- which a named style can express and an inline style cannot, since no pseudo-state can be written as a style attribute. A define carrying when blocks is given a generated class and the document emits a stylesheet for it.PREFIXnone (top-level)DATARequiredstyle-name (required) -- Style name (required). Must be a valid identifier that can be referenced from styles properties.CHILDREN
breakpointwhen
PROPERTIESAny property (Any CSS property name)Supports ? if: and ? else: conditionals.EXAMPLE
define: highlight
- background-color: #fef08a
- padding: 4px 8px

ptml:
> text: Important note
- styles: highlight
fontsDeclaration
Root node that loads web fonts from Google Fonts, so a prototype can use the real typeface rather than a lookalike whose metrics shift the line breaks. Each child is a font entry naming a family, optionally with the weights to load. Loaded fonts are then used through font-family in styles. Fonts are requested with font-display: block, so text waits briefly rather than painting once with fallback metrics and reflowing. If a font does not load for any reason -- a misspelled family, no network, a blocked request -- it is reported at runtime rather than silently falling back.PREFIXnone (top-level)DATANot accepted -- this node takes no inline data.PROPERTIES
font
EXAMPLE
fonts:
- font: Inter
- weights: 400 700
- font: Playfair Display
- weights: 400 italic

ptml:
> text: A headline in the real typeface
- styles:
- font-family: Playfair Display, serif
- font-weight: 400
functionDeclaration
Defines a reusable function with optional parameters. Functions can contain actions like set, list operations, and calls to other functions.PREFIXnone (top-level)DATARequiredfunction name (required) -- The name of the function. Cannot start with $. Function names must be unique within the document.parameter (optional) -- Optional parameter names. Parameters cannot start with $ and must be unique within the function. Parameters are available as variables within the function body.CHILDREN
rangeeach
Supports action children (! set, ! call, etc).EXAMPLE
state:
- count: 0

function: increment
! set: $count $count 1 | add

ptml:
> text: Count: $count
> button:
> text: Add one
- click:
! call: increment
importDeclaration
Root node for importing templates and named styles from another PTML file. The data is a simple filename (e.g. templates.ptml) with no path. Imported templates and defines become available as if declared in the current file. Imports are transitive: an imported file may import others, and everything reachable that way becomes available. Where two files declare the same name, the nearer one wins -- this document beats what it imports, and a nearer import beats a deeper one; between two imports in the same file, the later wins. Circular imports resolve rather than recursing. An import naming a file that was not supplied, or one that is not valid PTML, is an error rather than being quietly skipped.PREFIXnone (top-level)DATARequiredfilename (required) -- Simple filename with no path (e.g. templates.ptml)EXAMPLE
import: shared-styles.ptml

ptml:
> box:
> text: Content from a page that imports shared-styles.ptml
initDeclaration
May only contain call nodes; runs those function calls when PTML first renders.PREFIXnone (top-level)DATANot accepted -- this node takes no inline data.EXAMPLE
state:
- page: home

function: setPage
! set: $page getting-started

init:
! call: setPage

ptml:
> text: Current page: $page
ptmlDeclaration
The only renderable root node. Contains blocks that define the user interface. Optional: 0 or 1 per file.PREFIXnone (top-level)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).EXAMPLE
ptml:
> text: Hello, world!
recordListDeclaration
Defines a list of record items. Records contain key-value pairs as their children. Record items are written as "- record:" with key-value children.PREFIXnone (top-level)DATARequiredlist-name (required) -- Required list name. Must be a single word. Inline items are not supported - use child syntax with "- record:" instead.PROPERTIES
record
EXAMPLE
recordList: expenses
- record:
- name: Rent
- amount: 1200
- record:
- name: Food
- amount: 300

ptml:
> each: expenses as $expense
> text: $expense.name -- $expense.amount
stateDeclaration
A state node that declares state variables. State nodes have no data themselves, but contain children where each child type is a variable name (key). Children can be scalar key-value pairs (with data, no children), nested objects (with children, no data), or arrays (with children using -: prefix, no data). Variable names must be unique within a state node.PREFIXnone (top-level)DATANot accepted -- this node takes no inline data.PROPERTIESAny property
key-valuestate-objectstate-array
EXAMPLE
state:
- username: Alice
- score: 0

ptml:
> text: Player: $username
> text: Score: $score
templateDeclaration
Defines a reusable template with optional parameters. Templates can contain any renderable nodes (box, text, button, etc.) and are rendered via show nodes.PREFIXnone (top-level)DATARequiredtemplate name (required) -- The name of the template. Cannot start with $. Template names must be unique within the document.parameter (optional) -- Optional parameter names. Parameters cannot start with $ and must be unique within the template. Parameters are available as variables within the template body.CHILDRENAny block node (container parent).Supports ? if: and ? else: conditionals.EXAMPLE
template: badge label
> text: $label
- styles:
- background-color: #dbeafe
- padding: 4px 8px

ptml:
> show: badge New
> show: badge Sale
valueListDeclaration
Defines a list of simple string values. Value items are written as "- value" without a type prefix.PREFIXnone (top-level)DATARequiredlist-name (required) -- Required list name. Must be a single word. Inline items are not supported - use child syntax with "- value" (no type prefix) instead.PROPERTIES
value
EXAMPLE
valueList: fruits
- Apple
- Banana
- Cherry

ptml:
> each: fruits as $fruit
> text: $fruit

Block Nodes

Block nodes are renderable UI elements. They use the > prefix.
boxBlock
A container element that can hold other nodes. Optional role (main, header, footer, article, section, nav, aside) renders as that HTML element instead of div. Boxes can be nested and can contain text, buttons, other boxes, and control structures.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
stylesrole
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> box:
- role: main
> text: Welcome to the app
> box:
> text: Nested content goes here
breakpointBlock
Conditionally renders its children when the viewport matches the breakpoint condition. Data is a breakpoint label (e.g. small), "label or more", or "label or less"; the label must be one declared in a breakpoints declaration. A breakpoint block is an override layered on top of what surrounds it, never a replacement for it: breakpoints are resolved against the render context viewportWidth rather than by CSS media queries, so where no viewport is known -- server rendering, or a host that supplies none -- no breakpoint block renders and no breakpoint style applies. Whatever sits outside breakpoint blocks is therefore the fallback layout, and must stand on its own. Which end of the range that fallback describes is your choice: put mobile styles in the base and widen with "or more" for mobile-first, or the reverse for desktop-first.PREFIX> (angle bracket)DATARequiredbreakpoint-reference (required) -- Breakpoint label, "label or more", or "label or less". Must match a label defined in a breakpoints declaration.CHILDRENAny block node (container parent).PROPERTIESAny property (When under define: CSS properties. When in block context: N/A (block children only).)EXAMPLE
breakpoints:
- small: 768
- large:

define: card
- width: 100%
> breakpoint: large or more
- width: 50%

ptml:
> box:
- styles: card
> text: Responsive card
buttonBlock
A clickable button element that can contain text, styles, click handlers, and conditional disabling.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
text
PROPERTIES
stylesclickdisabled
Supports ? if: and ? else: conditionals.EXAMPLE
state:
- count: 0

ptml:
> button:
> text: Clicked $count times
- click:
! set: $count $count 1 | add
cellBlock
A table cell that renders as th or td based on the row section. Content comes from children (e.g. text, box). Only valid as a direct block child of a row node.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
styles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> table:
> row:
> cell:
> text: Name
> cell:
> text: Age
> row:
> cell:
> text: Alice
> cell:
> text: 30
checkboxBlock
A checkbox input field for forms. Checkboxes can be accessed via form.fieldName syntax (e.g., form.agree). A checkbox needs a binding so that its checked state goes somewhere: either an id, which binds it to form.<id>, or a value bound to a state variable (e.g. - value: $agree). Styles are optional. An id must be unique across the document, since two fields sharing one would share a single form value and produce duplicate ids in the page; the exception is fields in different branches of a conditional, which never render together. Inside an each or range, use a per-item id such as "- id: $item.key" rather than a fixed one, which would repeat for every item.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
idvaluestyles
EXAMPLE
state:
- agreed: false

ptml:
> checkbox:
- value: $agreed
circleBlock
A circle shape inside an svg, positioned by cx and cy with radius r.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
cxcyr (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> circle:
- cx: 12
- cy: 12
- r: 10
compileBlock
Renders a PTML string from state inline. The data field must be a state variable reference (e.g., $source). The resolved string is parsed as PTML and rendered within the parent context, sharing state.PREFIX> (angle bracket)DATARequiredstate variable (required) -- A state variable reference containing PTML source (e.g., $source).PROPERTIES
styles
EXAMPLE
state:
- source: > text: Hello from compiled PTML!

ptml:
> compile: $source
debugBlock
Displays debugging information showing the current state, lists, and loop context in a readable format. Useful for development and troubleshooting.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.EXAMPLE
state:
- username: Alice
- score: 42

ptml:
> debug:
eachBlock
Iterates over a list, rendering its children for each item. Can optionally bind the item and/or index to variables, and accepts the same children a box does. An optional sort child orders the items at render time without changing the list itself: "- sort: title" orders by that property ascending, "- sort: title desc" reverses it, and "- sort: asc" or "- sort: desc" orders a list of plain values by the values themselves. Numbers compare as numbers, so 10 sorts after 3, and text compares case-insensitively with embedded numbers read naturally, so "item 10" follows "item 9". Items missing the sorted property sort last ascending. Equal items keep their original order. To sort by a property actually named asc or desc, give the direction explicitly: "- sort: desc asc".PREFIX> (angle bracket)DATARequired (comma-separated)list specification (required) -- List name followed by optional "as $variable" to bind the item. Format: <listName> [as $variable]index specification (optional) -- Optional index binding. Format: index as $variableCHILDRENAny block node (container parent).PROPERTIES
stylessort
Supports ? if: and ? else: conditionals.Supports action children (! set, ! call, etc).EXAMPLE
recordList: tasks
- record:
- title: Wash up
- priority: 3
- record:
- title: Buy milk
- priority: 1

ptml:
> each: tasks as $task
- sort: priority
> text: $task.title
- newline:
ellipseBlock
An ellipse shape inside an svg, positioned by cx and cy with radii rx and ry.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
cxcyrx (required)ry (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> ellipse:
- cx: 12
- cy: 12
- rx: 10
- ry: 6
formBlock
A form container element that groups form inputs and buttons together. Form fields can be accessed via form.fieldName syntax (e.g., form.name, form.email). Forms can contain inputs, buttons, text, styles, and other container elements.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
styles
Supports ? if: and ? else: conditionals.EXAMPLE
state:
- username:
- email:

ptml:
> form:
> input:
- value: $username
- type: text
> input:
- value: $email
- type: email
> button:
> text: Submit
groupBlock
Groups shapes inside an svg so a transform, fill or stroke can be applied to all of them at once. Renders an SVG g element.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
pathcircleellipserectlinepolylinepolygongroup
PROPERTIES
fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> group:
- transform: translate(4 4)
- fill: currentColor
> path:
- d: M0 0h16
> path:
- d: M0 8h16
headerBlock
A heading element that renders as HTML h1–h6. Optional data specifies the level (h1–h6); default is h1. Content comes from children (e.g. text).PREFIX> (angle bracket)DATAOptionalheading level (optional) -- Optional h1, h2, h3, h4, h5, or h6. Default is h1.CHILDRENAny block node (container parent).PROPERTIES
styles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> header: h1
> text: Page Title
> header: h2
> text: Section Heading
imageBlock
An image element that displays a picture from a URL. Requires src (as a key-value child); can have optional alt text for accessibility and styles. Src can be a literal URL or a state reference (e.g. $imageUrl).PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
src (required)altstyles
EXAMPLE
ptml:
> image:
- src: https://example.com/photo.jpg
- alt: A scenic mountain view
inputBlock
A text input field element. Input fields are typically used within forms and can be accessed via form.fieldName syntax (e.g., form.name). An input needs a binding so that what is typed goes somewhere: either an id, which binds it to form.<id>, or a value bound to a state variable (e.g. - value: $name). Type is optional and defaults to text. Placeholder and styles are optional. Placeholder can be a literal string or a state reference (e.g. $hint). An id must be unique across the document, since two fields sharing one would share a single form value and produce duplicate ids in the page; the exception is fields in different branches of a conditional, which never render together. Inside an each or range, use a per-item id such as "- id: $item.key" rather than a fixed one, which would repeat for every item.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
idtypevalueplaceholderstyles
EXAMPLE
state:
- name:

ptml:
> input:
- value: $name
- type: text
- placeholder: Your name
> text: Hello, $name!
labelBlock
A form label that associates text with a form control for accessibility. Use optional "for" (id of the control) to associate with a control elsewhere, or wrap the control and text as children. The for value must name an id declared by a field in the same document, or the label links to nothing; a label may instead wrap its field and need no for at all. A for taken from state (e.g. $target) is resolved at render time.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
fortextstyles
EXAMPLE
state:
- email:

ptml:
> label:
- for: email
> text: Email Address
> input:
- id: email
- value: $email
lineBlock
A straight line inside an svg, from (x1, y1) to (x2, y2). A line has no fill, so it needs a stroke to be visible.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
x1 (required)y1 (required)x2 (required)y2 (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
- stroke: currentColor
> line:
- x1: 3
- y1: 12
- x2: 21
- y2: 12
linkBlock
A hyperlink element for external URLs or in-app navigation. Requires href (as a key-value child). Can have optional text, target (e.g. _blank), styles, and a click handler for in-app navigation (e.g. set: page shop with show: $page).PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
href (required)texttargetstylesclick
EXAMPLE
ptml:
> link:
- href: https://example.com
> text: Visit Example
listBlock
A list container that renders as an HTML ul (unordered) or ol (ordered) based on the optional type property. Use - type: ordered or - type: decimal for ordered lists; - type: lower-alpha, upper-alpha, lower-roman, upper-roman for ordered lists with that list style. Omit type for unordered (bullet) list. Only listItem nodes may be direct block children.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
listItem
PROPERTIES
typestyles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> list:
> listItem:
> text: First item
> listItem:
> text: Second item
> listItem:
> text: Third item
listItemBlock
A list item that renders as an HTML li element. Content comes from children (e.g. text, box). Typically used inside a list node.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).PROPERTIES
styles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> list:
> listItem:
> text: Buy groceries
> listItem:
> text: Walk the dog
optionBlock
An option element for select dropdowns. Option elements define the choices available in a select dropdown. The data field contains the display text (visible to the user), and a value key-value child specifies the option value. The display text is interpolated like any other text, so "$country.name" works for options generated inside an each, and the value child accepts a state reference in the same way.PREFIX> (angle bracket)PROPERTIES
value (required)styles
EXAMPLE
state:
- country: uk

ptml:
> select:
- value: $country
> option: United States
- value: us
> option: United Kingdom
- value: uk
> option: Germany
- value: de
pathBlock
A path shape inside an svg. The d attribute holds the path data copied straight from an icon set. Inherits fill and stroke from its svg unless it sets its own.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
d (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> path:
- d: M3 12h18
polygonBlock
A closed shape inside an svg, given as a points list. Unlike polyline the final point joins back to the first.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
points (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> polygon:
- points: 12 2 22 22 2 22
polylineBlock
A connected series of straight lines inside an svg, given as a points list (e.g. "20 6 9 17 4 12").PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
points (required)fillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
- fill: none
- stroke: currentColor
> polyline:
- points: 20 6 9 17 4 12
radioBlock
A radio button input for forms. Multiple radios with the same name form a group; form state holds the selected option value at form.name. Radios require name and value (as key-value children) and can have optional id, selected (state ref for two-way binding), and styles.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
name (required)value (required)idselectedstyles
EXAMPLE
state:
- size: medium

ptml:
> radio:
- name: size
- value: small
- selected: $size
> radio:
- name: size
- value: medium
- selected: $size
rangeBlock
A range node that iterates over a state variable. Range nodes must have at least one child.PREFIX> (angle bracket)DATARequired (comma-separated)range specification (required) -- State variable and index binding. Format: $stateVariable as $indexCHILDREN
range
Supports action children (! set, ! call, etc).EXAMPLE
state:
- gridSize: 3

valueList: items

function: populateItems
> range: gridSize as $i
! addValue: items $i

init:
! call: populateItems

ptml:
> each: items as $item
> text: Item number $item
rectBlock
A rectangle shape inside an svg. Width and height are required; x, y, rx and ry are optional.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
xywidth (required)height (required)rxryfillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
> rect:
- x: 3
- y: 3
- width: 18
- height: 18
- rx: 2
rowBlock
A table row that renders as HTML tr. Optional property - role: header | body | footer (default body). Only cell nodes may be direct block children. Only valid as a direct block child of a table node.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
cell
PROPERTIES
rolestyles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> table:
> row:
- role: header
> cell:
> text: Name
> cell:
> text: Score
> row:
> cell:
> text: Alice
> cell:
> text: 95
selectBlock
A dropdown select element for forms. Select fields are typically used within forms and can be accessed via form.fieldName syntax (e.g., form.country). A select needs a binding so that the chosen option goes somewhere: either an id, which binds it to form.<id>, or a value bound to a state variable. Takes one or more option children; styles are optional. An id must be unique across the document, since two fields sharing one would share a single form value and produce duplicate ids in the page; the exception is fields in different branches of a conditional, which never render together. Inside an each or range, use a per-item id such as "- id: $item.key" rather than a fixed one, which would repeat for every item. Options may be written out directly, or generated with an each over a list, and may be guarded by if/else or come from a template via show. A select must contain at least one of these; a select with no way to produce options renders a dropdown with nothing to choose. A list that happens to be empty at runtime is fine. Other block nodes are not allowed inside a select, because a select element may only hold options.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
optioneachifelseshow
PROPERTIES
idvaluestyles
Supports ? if: and ? else: conditionals.EXAMPLE
state:
- colour: blue

ptml:
> select:
- value: $colour
> option: Red
- value: red
> option: Green
- value: green
> option: Blue
- value: blue
> text: You picked: $colour
showBlock
Renders a template by name with optional arguments. The template name can be a literal name or a state variable reference (e.g., $page). Arguments can be given positionally after the template name, which is convenient for single words and variable references, or as named children -- one per parameter, e.g. "- label: Back in stock soon". Named arguments are the only way to pass a value containing spaces, because positional arguments are separated by spaces; they can also be given in any order and omitted individually. The two styles cannot be mixed in one call. A parameter with no argument is empty, so templates can treat parameters as optional.PREFIX> (angle bracket)DATARequiredtemplate name (required) -- The name of the template to render, or a state variable reference (e.g., $page) for dynamic template selection.argument (optional) -- Optional argument values to pass to the template. Can be literal values or variable references (e.g., $contact).PROPERTIESAny property (styles, plus one child per template parameter (e.g. "- label: Back in stock soon").)EXAMPLE
template: greeting name
> text: Hello, $name!

ptml:
> show: greeting World
svgBlock
An inline SVG, for icons and simple vector graphics. Requires a viewBox and contains shape children (path, circle, ellipse, rect, line, polyline, polygon, group). Because it renders inline rather than as an image, a fill or stroke of currentColor takes the surrounding text colour, and styles applies to it like any other block. An optional title gives it an accessible name; without one it is marked decorative.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
pathcircleellipserectlinepolylinepolygongroup
PROPERTIES
viewBox (required)widthheighttitlefillfill-opacityfill-ruleclip-rulestrokestroke-widthstroke-linecapstroke-linejoinstroke-dasharraystroke-dashoffsetstroke-opacityopacitytransformstyles
EXAMPLE
ptml:
> svg:
- viewBox: 0 0 24 24
- fill: none
- stroke: currentColor
- stroke-width: 2
- title: Done
> polyline:
- points: 20 6 9 17 4 12
tableBlock
A table container that renders as HTML table. Groups row children by role (header/body/footer) into thead, tbody, tfoot. Only row nodes may be direct block children.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.CHILDREN
row
PROPERTIES
styles
Supports ? if: and ? else: conditionals.EXAMPLE
ptml:
> table:
> row:
- role: header
> cell:
> text: City
> cell:
> text: Population
> row:
> cell:
> text: London
> cell:
> text: 9 million
textBlock
Displays text content, with interpolated expressions using $variable syntax and pipe expressions, and optional inline styles. A text node may contain further text nodes, which render as inline runs of the same paragraph -- the way to style part of a sentence, such as one magenta word in a headline. Runs flow inline, so wrapping, line height and the width of a space are the browser's rather than something to approximate with a flex row and a gap. Whitespace: exactly one space after the colon separates the node from its text, and any further leading spaces are part of the text -- which is how a run is spaced away from the one before it. Trailing spaces are always stripped, because they are invisible in the source and editors routinely remove them on save, so no document should depend on them.PREFIX> (angle bracket)CHILDREN
text
PROPERTIES
stylesnewline
EXAMPLE
ptml:
> text: Open from
> text: 9am
- styles:
- color: magenta
> text: to 5pm
textareaBlock
A multi-line text input field element. Textarea fields are typically used within forms and can be accessed via form.fieldName syntax (e.g., form.description). A textarea needs a binding so that what is typed goes somewhere: either an id, which binds it to form.<id>, or a value bound to a state variable (e.g. - value: $notes). Placeholder and styles are optional. Placeholder can be a literal string or a state reference (e.g. $hint). An id must be unique across the document, since two fields sharing one would share a single form value and produce duplicate ids in the page; the exception is fields in different branches of a conditional, which never render together. Inside an each or range, use a per-item id such as "- id: $item.key" rather than a fixed one, which would repeat for every item.PREFIX> (angle bracket)DATANot accepted -- this node takes no inline data.PROPERTIES
idvalueplaceholderstyles
EXAMPLE
state:
- notes:

ptml:
> textarea:
- value: $notes
- placeholder: Your notes
> text: You wrote: $notes
whenBlock
Styles that apply only while an element is in a given interaction state: hover, focus, active, disabled, placeholder. Valid only inside a define, because these become a real CSS rule attached to a generated class for that named style -- no pseudo-state can be expressed as an inline style, which is how every other PTML style is applied. focus means :focus-visible, so a focus ring appears for keyboard users without sticking to a button after a mouse click. Pair any hover style with a focus style: an affordance only a pointer can reach is invisible to anyone navigating by keyboard.PREFIX> (angle bracket)DATARequiredinteraction-state (required) -- One of: hover, focus, active, disabled, placeholderPROPERTIESAny property (CSS properties to apply while in this state.)EXAMPLE
define: card
- background-color: #ffffff
- transition: background-color 0.15s ease

> when: hover
- background-color: #f4f4f5
> when: focus
- outline: 2px solid #2563eb
- outline-offset: 2px

ptml:
> button:
- styles: card
> text: Open

Property Nodes

Property nodes configure their parent node. They use the - prefix.
clickProperty
Defines click handler actions for buttons and other clickable elements. Contains one or more actions to execute when clicked.PREFIX- (dash)DATANot accepted -- this node takes no inline data.Supports action children (! set, ! call, etc).EXAMPLE
state:
- name: Dave

ptml:
> button:
> text: Change name
- click:
! set: $name Alice
> text: Hello, $name!
fontProperty
A single font family to load, named in the data (e.g. "- font: Playfair Display"). An optional weights child lists the weights to load and whether italics are needed. Only valid inside a fonts declaration.PREFIX- (dash)DATARequiredfamily (required) -- The font family name exactly as Google Fonts spells it, e.g. Inter or Playfair Display.PROPERTIES
weights
EXAMPLE
fonts:
- font: Inter
- weights: 400 700

ptml:
> text: Hello
- styles:
- font-family: Inter, sans-serif
recordProperty
A record node that stores key-value pairs. Records have no data themselves, but contain children where each child type is a key and child data is the value. Keys must be unique within a record.PREFIX- (dash)DATANot accepted -- this node takes no inline data.PROPERTIESAny property
key-value (required)
EXAMPLE
recordList: contacts
- record:
- name: Alice
- email: alice@example.com
- record:
- name: Bob
- email: bob@example.com
roleProperty
Property that specifies a row section: header, body, or footer. Used as child of row. Default is body.PREFIX- (dash)DATAOptionalrole (optional) -- Optional header, body, or footer. Default is body.EXAMPLE
ptml:
> box:
- role: nav
> text: Navigation content
stylesProperty
Property node that applies CSS styles to elements. Can optionally reference a named style defined with define: or define inline styles. Styles can contain CSS properties (e.g., color, font-size) and conditional styles using if/else nodes.PREFIX- (dash)DATAOptionalstyle-name (optional) -- Optional reference to a named style defined with define:. When present, must be a valid identifier that matches a defined style name.PROPERTIESAny propertySupports ? if: and ? else: conditionals.EXAMPLE
ptml:
> text: Styled text
- styles:
- color: white
- background-color: #1e40af
- padding: 8px 16px
whereProperty
Specifies a condition to match items in a list. Used as a child of updateRecord to find items by property value.PREFIX- (dash)DATARequiredcondition (required) -- Required condition expression in format "PROPERTYNAME is MATCHVALUE". The property name can be a simple property (e.g., "id") or nested property (e.g., "contact.id"). The match value can be a literal or variable reference (e.g., "$contactId").EXAMPLE
recordList: tasks
- record:
- id: 1
- title: Buy milk
- done: false

ptml:
> button:
> text: Mark task 1 done
- click:
! updateRecord: tasks
- where: id is 1
- record:
- done: true

Conditional Nodes

Conditional nodes control rendering. They use the ? prefix.
elseConditional
Provides alternative content when the preceding sibling if node condition is false. Must be paired with a sibling if node.PREFIX? (question mark)DATANot accepted -- this node takes no inline data.CHILDRENAny block node (container parent).Supports ? if: and ? else: conditionals.EXAMPLE
state:
- loggedIn: false

ptml:
? if: $loggedIn
> text: Welcome back!
? else:
> text: Please log in.
ifConditional
Conditionally renders its children based on a condition. The condition can be a simple variable check or a comparison expression.PREFIX? (question mark)DATARequiredcondition (required) -- Required condition expression. Can be a simple variable (e.g., $isActive) or a comparison expression (e.g., $variable is value). Variables must start with $.CHILDRENAny block node (container parent).Supports ? if: and ? else: conditionals.EXAMPLE
state:
- showDetails: true

ptml:
> text: Product Name
? if: $showDetails
> text: This product is available in three sizes.

Action Nodes

Action nodes modify state. They use the ! prefix and must appear inside a click handler, init block, or function body.
addRecordAction
Adds a record to a list. The record is defined as a child node.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to add the record toPROPERTIES
record (required)
EXAMPLE
recordList: tasks
- record:
- title: Buy milk

ptml:
> each: tasks as $task
> text: $task.title
> button:
> text: Add task
- click:
! addRecord: tasks
- record:
- title: New task
addValueAction
Adds a simple value to a list. The value can be an expression with variables, pipes, etc.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to add the item toitem-value (required) -- Required item value to add to the list (expression with variables, pipes, etc.)EXAMPLE
valueList: tags
- urgent

ptml:
> each: tags as $tag
> text: $tag
> button:
> text: Add tag
- click:
! addValue: tags new-tag
callAction
Calls a function with optional arguments. The function name can be a literal function name or a variable (starting with $) that resolves to a function name.PREFIX! (exclamation)DATARequiredfunction-name (required) -- The name of the function to call. Can be a literal function name or a variable (starting with $) that resolves to a function name. Variables can reference state variables, loop variables, list names, or function parameters.argument (optional) -- Optional arguments to pass to the function. Each argument is an expression that can include variables, pipes, and other operations.EXAMPLE
state:
- count: 0

function: increment
! set: $count $count 1 | add

ptml:
> button:
> text: Count: $count
- click:
! call: increment
clearAction
Clears a state variable or form field by setting it to an empty stringPREFIX! (exclamation)DATARequiredvariable-name (required) -- The name of the state variable or form field to clear (with or without $ prefix)EXAMPLE
state:
- search:

ptml:
> input:
- value: $search
> button:
> text: Clear
- click:
! clear: $search
> text: Search: $search
getRecordAction
Gets a record from a list at a specific index and makes it available as a loop variable. The index can be a number or a variable reference. The retrieved record is assigned to the specified variable name for use in subsequent nodes.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to get the record fromindex (required) -- The index (number or variable reference starting with $) of the record to retrievevariable-binding (required) -- Variable binding in format "as $variableName" to assign the retrieved recordEXAMPLE
recordList: users
- record:
- name: Alice
- record:
- name: Bob

valueList: indices
- 0
- 1

ptml:
> each: indices as $i
! getRecord: users $i as $user
> text: $user.name
getValueAction
Gets a value from a list at a specific index and makes it available as a loop variable. The index can be a number or a variable reference. The retrieved value is assigned to the specified variable name for use in subsequent nodes.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to get the value fromindex (required) -- The index (number or variable reference starting with $) of the value to retrievevariable-binding (required) -- Variable binding in format "as $variableName" to assign the retrieved valueEXAMPLE
valueList: colours
- Red
- Green
- Blue

valueList: indices
- 0
- 1
- 2

ptml:
> each: indices as $i
! getValue: colours $i as $colour
> text: $colour
removeRecordAction
Removes a record from a list by variable reference. Typically used within each loops where the record is available as a loop variable.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to remove the record fromitem-variable (required) -- Required item variable value (must start with $) to the record to remove. Must specify both list name and item variable. Use format: removeRecord: <listName> $itemEXAMPLE
recordList: items
- record:
- name: Apple
- record:
- name: Banana

ptml:
> each: items as $item
> text: $item.name
> button:
> text: Remove
- click:
! removeRecord: items $item
removeValueAction
Removes a value from a list by matching the value directly.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to remove the value fromitem-value (required) -- The value expression to remove from the listEXAMPLE
valueList: tags
- urgent
- review
- done

ptml:
> each: tags as $tag
> text: $tag
> button:
> text: Remove
- click:
! removeValue: tags $tag
setAction
Sets a state variable to a value. The variable name must start with $. The value can be a literal or an expression using pipes. A form field is read back with $form.<field>, where the field is an input, textarea, select or checkbox id, or a radio group name -- for example "! set: $email $form.email". Written without the $ it is the literal text "form.email", not the contents of the field.PREFIX! (exclamation)DATARequiredvariable-name (required) -- The state variable to set (must start with $)value-expression (optional) -- Optional value expression. Can be a literal value or an expression using pipes. Variables in the expression must be defined in state, available in the current loop context, be list names, or be function parameters.EXAMPLE
state:
- greeting: Hello

ptml:
> text: $greeting
> button:
> text: Change greeting
- click:
! set: $greeting Goodbye
setRecordAction
Sets a record in a list at a specific index. The index can be a number or a variable reference. The record is defined as a child node.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to set the record inindex (required) -- The index (number or variable reference starting with $) where to set the recordPROPERTIES
record (required)
EXAMPLE
recordList: users
- record:
- name: Alice

state:
- index: 0

ptml:
> button:
> text: Update user
- click:
! setRecord: users $index
- record:
- name: Bob
setValueAction
Sets a value in a list at a specific index. The index can be a number or a variable reference. The value can be an expression with variables, pipes, etc.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to set the value inindex (required) -- The index (number or variable reference starting with $) where to set the valuevalue-expression (required) -- The value expression to set at the specified index (can include variables, pipes, etc.)EXAMPLE
valueList: items
- Apple
- Banana

state:
- index: 0

ptml:
> button:
> text: Replace first item
- click:
! setValue: items $index Cherry
updateRecordAction
Updates a record in a list by finding it using a where clause and replacing it with a new record. The where clause specifies which property to match, and the record child defines the new values.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to update a record inPROPERTIES
where (required)record (required)
EXAMPLE
recordList: contacts
- record:
- id: 1
- name: Alice

state:
- newName: Bob

ptml:
> button:
> text: Rename to $newName
- click:
! updateRecord: contacts
- where: id is 1
- record:
- name: $newName
updateValueAction
Updates a value in a list at a specific index. The index can be a number or a variable reference. The value can be an expression with variables, pipes, etc.PREFIX! (exclamation)DATARequiredlist-name (required) -- The name of the list to update the value inindex (required) -- The index (number or variable reference starting with $) where to update the valuevalue-expression (required) -- The value expression to set at the specified index (can include variables, pipes, etc.)EXAMPLE
valueList: items
- Apple
- Banana

state:
- index: 0

ptml:
> button:
> text: Update first item
- click:
! updateValue: items $index Cherry
windowAction
Performs a browser window operation. Currently supports scrollTop to scroll the page to the top. Extensible for future operations.PREFIX! (exclamation)DATARequiredoperation (required) -- The window operation to perform. Supported: scrollTop.EXAMPLE
state:
- page: home

ptml:
> button:
> text: Back to top
- click:
! window: scrollTop