# YAML Guide
RusDox is designed so the YAML reads like the document.
The basic shape is always:
```yaml
version: 1
output_name: my-document
blocks:
- type: title
text: My Document
- type: body
text: This is a paragraph.
```
## Top-Level Fields
version
- Required for new files
- Current value: 1
- Use rusdox migrate legacy.yaml --in-place for legacy unversioned files
`output_name`
- Optional
- Controls the file name of the generated output
- If omitted, RusDox uses the spec file stem
`metadata`
- Optional
- Controls DOCX package properties such as title, author, subject, keywords, and custom properties
`blocks`
- Required for real documents
- Ordered from top to bottom
- This is the document structure
`styles`
- Optional
- Defines reusable named paragraph, run, and table styles
- Supports inheritance through `based_on`
- Paragraph styles can also set `next`
`variables`
- Optional
- Defines reusable values for nested interpolation, repeat blocks, and bounded conditions
## Metadata
Use metadata when generated documents need clean properties in Word, search tools, or downstream automation.
```yaml
metadata:
title: Client Rollout Plan
author: RusDox Studio
subject: Q4 regional rollout
language: en-US
keywords:
- rollout
- planning
custom_properties:
Client: Northwind Health
Sponsor: Maya Chen
```
Supported metadata fields:
- `title`
- `author`
- `subject`
- `language`: a BCP 47-style document language such as `en-US`, `fr`, or `mul`
- `keywords`
- `custom_properties`
`custom_properties` values are stored as string custom properties in the DOCX package.
## Variables, Includes, And Repeaters
Use these features when one YAML file would otherwise become repetitive.
Variables use `{{name}}` placeholders:
```yaml
variables:
client: Northwind Health
quarter: Q4 2026
blocks:
- type: title
text: "{{client}} Rollout Plan"
- type: subtitle
text: "{{quarter}} program snapshot"
```
Includes inline reusable block fragments relative to the current YAML file:
```yaml
blocks:
- type: include
path: fragments/summary.yaml
variables:
sponsor: Maya Chen
```
An included YAML fragment can be:
- a single block mapping
- a sequence of blocks
- a mapping with optional `variables` plus `blocks`
Repeaters expand a block template for each item in a sequence:
```yaml
variables:
regions:
- name: North America
owner: Maya
- name: EMEA
owner: Leon
blocks:
- type: repeat
variable: regions
as: region
blocks:
- type: section
text: "{{region.name}}"
- type: body
text: "Owner: {{region.owner}}"
```
Supported repeat fields:
- `variable`: name of a sequence variable to iterate
- `items`: inline sequence to iterate
- `as`: loop variable name, defaults to `item`
- `blocks`: template block list
Each repeat iteration also exposes:
- `repeat_index`: zero-based index
- `repeat_number`: one-based index
## Conditions, filters, and escaping
Use a bounded when block for truthiness or exact scalar equality:
variables:
customer:
active: true
blocks:
- type: when
path: customer.active
equals: true
blocks:
- type: body
text: Active customer
otherwise:
- type: body
text: Inactive customer
Expressions support nested mapping/array paths and exactly five deterministic
filters: upper, lower, title, trim, and default("text").
text: "{{ customer.name | trim | title }}"
text: "{{ customer.owner | default(\"unassigned\") | upper }}"
Write four opening braces and four closing braces for literal double braces.
Expression values always become document text; they cannot inject raw OOXML or
execute code. The same expression behavior is available to JSON and TOML specs.
See [Spec Versioning and Authoring Tools](spec-versioning.md).
## Named Styles
Use named styles when the document needs stable reusable formatting instead of repeating direct properties on every block.
Example:
```yaml
styles:
paragraph:
- id: lead
based_on: Normal
next: body
paragraph:
alignment: center
spacing_after: 180
keep_next: true
run:
bold: true
color: "0F172A"
font_family: Georgia
font_size: 28
run:
- id: accent
based_on: DefaultParagraphFont
properties:
italic: true
color: "AA5500"
table:
- id: grid
based_on: TableNormal
properties:
width: 9360
blocks:
- type: paragraph
spec:
style_id: lead
runs:
- text: Styled
- text: " emphasis"
style_id: accent
- type: table
spec:
style_id: grid
columns:
- label: Metric
width: 4680
rows:
- cells:
- kind: text
text: ARR
```
Style definition groups:
- `styles.paragraph`
- `styles.run`
- `styles.table`
Paragraph style fields:
- `id`
- `name`
- `based_on`
- `next`
- `paragraph.list`
- `paragraph.alignment`
- `paragraph.spacing_before`
- `paragraph.spacing_after`
- `paragraph.keep_next`
- `paragraph.page_break_before`
- `run.bold`
- `run.italic`
- `run.underline`
- `run.strikethrough`
- `run.small_caps`
- `run.shadow`
- `run.color`
- `run.font_family`
- `run.font_size`
- `run.vertical_align`
Run style fields:
- `id`
- `name`
- `based_on`
- `properties.bold`
- `properties.italic`
- `properties.underline`
- `properties.strikethrough`
- `properties.small_caps`
- `properties.shadow`
- `properties.color`
- `properties.font_family`
- `properties.font_size`
- `properties.vertical_align`
Table style fields:
- `id`
- `name`
- `based_on`
- `properties.width`
- `properties.borders`
Units:
- paragraph spacing and table widths use twips
- run `font_size` uses OOXML half-points, so `24` means `12pt`
## Simple Block Types
Use these when you want readable documents with strong defaults:
- `cover_title`
- `title`
- `subtitle`
- `hero`
- `centered_note`
- `page_heading`
- `section`
- `body`
- `tagline`
- `image`
- `logo`
- `signature`
- `chart`
- `spacer`
Example:
```yaml
blocks:
- type: cover_title
text: Board Report
- type: subtitle
text: March 2026
- type: hero
text: Prepared automatically with RusDox
- type: centered_note
text: Internal use only
- type: page_heading
text: Board Narrative
- type: body
text: Revenue expanded faster than forecast.
```
`page_heading` starts a new page before the heading.
`spacer` adds vertical space when you want breathing room between sections.
## Visual Blocks
Use visual blocks when the document needs brand marks, screenshots, signatures, or SVG charts.
Example:
```yaml
- type: logo
path: ../assets/rusdox-mark.svg
alt_text: RusDox logo
max_width_twips: 2200
- type: image
path: ../assets/template-gallery.png
alt_text: RusDox template gallery
max_width_twips: 7200
- type: chart
path: ../assets/benchmark-stress-1000-pages.svg
alt_text: RusDox benchmark chart
max_width_twips: 7200
- type: signature
path: ../assets/signature-demo.svg
alt_text: Automated approval signature
max_width_twips: 2800
```
Supported visual fields:
- `path`
- `alt_text`
- `alignment`: `left`, `center`, `right`, `justified`
- `width_twips`
- `height_twips`
- `max_width_twips`
- `max_height_twips`
Supported file formats:
- PNG
- JPEG
- SVG
`alt_text` is required and must be meaningful. It is preserved in the DOCX
drawing properties and checked against the PDF semantic projection; a file name
or blank string does not satisfy validation. See the
[international and accessibility contract](international-accessibility.md).
## List Blocks
### `bullets`
```yaml
- type: bullets
items:
- Confirm launch date
- Finalize sales enablement
- Publish support macros
```
### `label_values`
Good for notes, metadata, meeting headers, and document summaries.
```yaml
- type: label_values
items:
- label: Date
value: 2026-03-10
- label: Owner
value: Operations
```
### `metrics`
Good for dashboard-style cards.
Available tones:
- `positive`
- `neutral`
- `warning`
- `risk`
```yaml
- type: metrics
items:
- label: ARR
value: $18.7M
tone: positive
- label: Cash Runway
value: 24 mo
tone: warning
```
## Tables
Tables use explicit columns and rows.
Each column needs:
- `label`
- `width`
Each row has `cells`.
Each cell can be:
- `kind: text`
- `kind: status`
Example:
```yaml
- type: table
spec:
style_id: grid
columns:
- label: Category
width: 2800
- label: Current
width: 2000
- label: Status
width: 1600
rows:
- cells:
- kind: text
text: ARR
- kind: text
text: $18.7M
- kind: status
text: Strong
tone: positive
```
`width` values are DOCX table widths in twips.
You usually only need to copy a working table from an example and edit the content.
## Custom Paragraphs
Use `paragraph` when you need mixed formatting inside one paragraph.
Example:
```yaml
- type: paragraph
spec:
alignment: center
spacing_after_twips: 120
runs:
- text: "This is "
- text: important
bold: true
- text: " and "
- text: styled
italic: true
color: D00000
```
Supported paragraph fields:
- `runs`
- `style_id`
- `alignment`: `left`, `center`, `right`, `justified`
- `spacing_before_twips`
- `spacing_after_twips`
- `page_break_before`
- `section_break_before`
Supported run fields:
- `text`
- `style_id`
- `bold`
- `italic`
- `underline`
- `strikethrough`
- `small_caps`
- `shadow`
- `color`
- `font_family`
- `size_pt`
- `vertical_align`
- `hyperlink`: `https://`, `http://`, `mailto:`, or `#bookmark`
- `bookmark`: 1–40 ASCII letters, digits, or underscores
- `field`: currently `table_of_contents`
- `footnote`
Underline values:
- `single`
- `double`
- `dotted`
- `dash`
- `wavy`
- `words`
- `none`
Vertical align values:
- `superscript`
- `subscript`
- `baseline`
## Shared Page Controls And Semantic Blocks
`page_setup`, `header`, `footer`, and `page_numbering` are consumed by both DOCX and PDF. Orientation is explicit and must agree with width and height.
```yaml
page_setup:
width_twips: 15840
height_twips: 12240
orientation: landscape
margin_top_twips: 1080
margin_right_twips: 900
margin_bottom_twips: 1080
margin_left_twips: 900
header:
text: Quarterly review
alignment: right
footer:
text: Page {page} of {pages}
alignment: center
page_numbering:
start_at: 3
format: lower_roman
blocks:
- type: table_of_contents
title: Contents
- type: page_break
- type: section_break
```
The DOCX TOC is an updateable Word field. PDF emits the heading list known from the spec at render time and does not invent page numbers.
## Rich Table Rows And Cells
Rows accept `repeat_as_header` and `allow_split_across_pages`. A `kind: rich` cell accepts `paragraphs`, horizontal `grid_span`, `background_color`, and an optional `nested_table`. See [`dual_output_contract.yaml`](../examples/dual_output_contract.yaml) for a complete, parity-tested example.
## Best Practices
- Use variables for repeated values, not for every sentence in the document.
- Reach for `include` and `repeat` before moving to Rust if the workflow is still mostly static content.
- Prefer `title`, `section`, `body`, `bullets`, `metrics`, and `table` before reaching for custom paragraphs.
- Let config control styling instead of repeating style values everywhere.
- Copy a close example and edit the content.
## See Real Files
- [../examples/board_report.yaml](../examples/board_report.yaml)
- [../examples/executive_dashboard.yaml](../examples/executive_dashboard.yaml)
- [../examples/dual_output_contract.yaml](../examples/dual_output_contract.yaml)
- [../examples/international_scripts.yaml](../examples/international_scripts.yaml)
- [../examples/formatting_showcase.yaml](../examples/formatting_showcase.yaml)
- [../examples/named_styles_showcase.yaml](../examples/named_styles_showcase.yaml)
- [../examples/yaml_composition_showcase.yaml](../examples/yaml_composition_showcase.yaml)