Every block - whether you drag it onto the canvas in the Layout Editor or write it by hand in a layout JSON file - shares a common shape, plus properties specific to its type.
Common properties
| Property | Meaning |
|---|---|
| Position (x_mm, y_mm) | Top-left corner, in mm from the page's top-left |
| Size (width_mm, height_mm) | Block's bounding box, in mm |
| Page (page) | Which page of the canvas (default 1) - see Layout Editor |
| Condition (condition) | Hide the block unless an expression is true - see Scripting & Processing |
| Z-index (z_index) | Paint order - higher draws on top. In the editor, use Send to Front/Back instead of typing a number |
Text block
The workhorse block - a label, a price callout, a paragraph, a formatted list.
- Content is a template: plain text is shown literally; anything wrapped in
{...}runs as real JavaScript against the submitted data, for anything dynamic. A bare field name on its own ({sale_price}) resolves directly to that field's value; anything more complex needs theinputs.prefix, since at that point it's genuine JavaScript, not a placeholder shortcut:- Formatted currency:
{'$' + inputs.sale_price.toLocaleString()} - Uppercase:
{inputs.make.toUpperCase()} - Combining fields:
{inputs.year + ' ' + inputs.make + ' ' + inputs.model}
- Formatted currency:
- If the result contains a line break, it automatically wraps as a multi-line
paragraph instead of a single fitted line - this is how you render a
variable-length list from a repeating group, by joining rows with
\n:{ inputs.options.map(function(o){ return o.description + ' - $' + o.price; }).join('\n') } - Style: font size, font weight (normal/bold), font family (see Assets & Fonts), text color, background color, horizontal/vertical alignment, and overflow behavior (clip, shrink-to-fit, or show past the box). Shrink-to-fit only applies to single-line text, not wrapped paragraphs - give a list-style block a generously tall box instead.
Image block
A logo, branding art, or a field-bound photo (a file- or signature-type field's
submitted value).
- Source: either a static asset from the project's
assets/folder, or a field ID to pull the image from what was submitted. - Fit: contain (default, no distortion, may letterbox), fill (stretches to exactly fill the box), cover (fills the box, cropping overflow), or none.
- Both raster (PNG/JPEG) and SVG work. A missing asset or unfilled field simply omits the block - no error.
Box block
A filled and/or stroked rectangle - colored panels, dividers, borders behind other content.
- Fill color and border color (both optional - a box with neither is invisible, useful as a purely structural spacer).
- Border width and border radius (rounded corners).
Signature block
Renders a captured signature (from a signature-type field) at a fixed position and
size, preserving its proportions (contain-style fit, no stretching or cropping).
QR code block
Encodes text/a URL as a QR code, always rendered as a centered square within the box (a non-square box just adds padding on the longer side, never distorts the code).
- Data: same template syntax as a text block's content - static text, or
{...}for anything built from submitted data (e.g. a per-vehicle lookup URL). - Colors: foreground (module color) and an optional background fill; omit the background for a transparent one.
- Empty data quietly omits the block, same as a missing image.
Grid block
A real table - for content that needs actual columns, not just a joined multi-line string (an options list with right-aligned prices, a spec table, anything tabular).
- Data: a JavaScript expression that evaluates to an array. Each array element
becomes one cell, flowing left to right and wrapping to a new row every N cells
(N = the grid's column count). Unlike a text or QR block's content, a grid's
datais the bare expression itself - no{...}wrapper around the whole thing, same convention as a block'scondition. - Columns: how many cells per row.
- Column widths: optional relative weights (e.g.
[3, 1]gives the first column 3× the second's width); omit for equal-width columns.
The simplest possible grid is just a flat, hand-written list of values - with
"columns": 2, this lays out as 3 rows:
"data": "['Description', 'Price', 'Extended Warranty', '$500', 'Paint Protection', '$250']"
A plain value like that renders as-is with the grid's default styling. Wrap a cell in
an object instead when you want to override styling for just that one cell -
using real HTML/CSS property names (background-color, color, font-family,
font-size, padding, text-align, vertical-align, border-color,
border-width) rather than this app's usual style naming, since that's the
vocabulary most people already associate with per-cell styling:
"data": "[{text: 'Description'}, {text: 'Price', 'text-align': 'right'}, {text: 'Extended Warranty'}, {text: '$500', 'text-align': 'right'}, {text: 'Paint Protection', 'background-color': '#ffeecc', 'border-color': '#cc8800', 'border-width': 0.5}, {text: '$250', 'text-align': 'right'}]"
(Note the quotes around hyphenated keys like 'text-align' - that's plain JavaScript
object-literal syntax, not anything Yatate-specific.)
- Grid-level style: the same property names as a cell override, applied as the
default for every cell - set these once instead of repeating them on every cell
(e.g.
text-align: rightat the grid level instead of on each price cell above). - Border: an optional
border-color/border-widthat the grid level draws a single shared line between every cell (like a normal table). A cell that overridesborder-colorand/orborder-widthitself gets its own border box instead - useful for highlighting one cell (e.g. a "best value" row) without adding lines everywhere. Only cells that actually set one of these two properties get a per-cell border; every other cell keeps using the plain shared grid lines. - Row height is content-driven - a row is exactly as tall as its content needs, and the grid itself grows to fit however many rows there are. It is not clipped to whatever height you set when placing the block; that's only its nominal starting box in the editor. If a grid can grow arbitrarily large (bound to a repeating group with no row limit), leave generous empty space below it on the page.
- Min/max rows: optional
min_rows/max_rowsbound the row count at generation time -max_rowstruncates extra rows so a grid never overflows a fixed layout space,min_rowspads with blank rows so a mostly-empty table doesn't collapse down to one or two rows. Both are set in the Layout Editor's grid properties panel, or directly in the JSON as siblings ofcolumns. Neither is set by default - row count stays purely content-driven unless you opt in. - No splitting across pages - a grid always renders fully on the page it starts on.
Typing the array out by hand like this works fine for a fixed spec table, but most real uses need the data built from a repeating group instead - see Generating Grid Data for that.