OPF
SpecDefinition referenceView source

Variables

Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. Organization logos are slide-scoped whole-field images: 'var:organization.logo', 'var:organization.logo.<shape>' (full, stacked, icon, wordmark), 'var:organization.<id>.logo' and 'var:organization.<id>.logo.<shape>' resolve per slide to the onLight or onDark asset for its background. See docs/templates-and-variables.md.

Metadata

Definition
#/$defs/Variables
Form
Dictionary
Source
spec/schemas/opf.schema.json

Field index

This definition uses the forms or value constraints below.

Complete definition constraints
Schema constraints
{
  "type": "object",
  "propertyNames": {
    "pattern": "^[a-z][a-z0-9-]*$"
  },
  "additionalProperties": {
    "$ref": "#/$defs/Variable"
  },
  "description": "Named variables, keyed by stable kebab-case id. A variable is a typed, named value the deck declares once and uses in many places: a color ('var:<id>' in color fields, the original use), or content that fills a template (text, number, date, image, url, list). Content is referenced inline as '{{<id>}}' inside any string, or whole as 'var:<id>' in a field of the matching type; '\\{{' writes a literal '{{'. A hex string is shorthand for a color variable. A variable with no 'value' is unfilled: expected in a template (root 'template': true), an error in a normal deck. Engines resolve variables with resolveVariables() before composing, rendering or exporting, so a template plus its values previews and exports exactly like the equivalent hand-written deck. Read-only built-in variables come from the deck's own metadata and use dotted names that a user id can never take: 'deck.name', 'deck.description', 'deck.author', 'speaker.name|title|email|phone|bio|photo' (first speaker), 'speakers' (every speaker's name, a list), 'organization.name|legalName|tagline|domain|email|phone' (primary organization), and 'speaker.<id>.<field>' / 'organization.<id>.<field>' by id. They are written the same two ways ('{{speaker.name}}', 'var:speaker.photo'), are never declared, and an unknown built-in path is a validation error; a known one with no source value resolves to nothing with a warning. The id 'speakers' is reserved. Three built-ins are slide-scoped and written only as inline tokens: '{{slide.number}}' (the displayed slide number after pagination), '{{slide.section}}' (the slide's section) and '{{deck.slideCount}}' (the number of slides after pagination). resolveVariables() leaves them as written; they resolve per slide as it is composed (resolveSlideVariables(), and header/footer text in layoutFurniture()), so every 'slide.' name is reserved and 'var:slide.*' or 'var:deck.slideCount' is a validation error. Organization logos are slide-scoped whole-field images: 'var:organization.logo', 'var:organization.logo.<shape>' (full, stacked, icon, wordmark), 'var:organization.<id>.logo' and 'var:organization.<id>.logo.<shape>' resolve per slide to the onLight or onDark asset for its background. See docs/templates-and-variables.md.",
  "examples": [
    {
      "risk": "#B42318",
      "highlight": {
        "type": "color",
        "value": "#0F4C81",
        "description": "Brand highlight for emphasized runs."
      }
    },
    {
      "client-name": {
        "type": "text",
        "example": "Acme Corp",
        "description": "Customer the deck is for."
      },
      "revenue": {
        "type": "number",
        "example": 1250000,
        "format": "$#,##0"
      },
      "review-date": {
        "type": "date",
        "example": "2026-10-01"
      }
    }
  ]
}