Design Resolution
How an engine decides the effective design for any given slide. The schema spreads these rules across field descriptions; this page states them once, as an algorithm.
Precedence
For every design field independently, the most specific source wins:
wins +--------------------------------------------------------+
^ | 1. slide design slides[i].design.* |
| +--------------------------------------------------------+
| | 2. deck design design.* on the presentation root |
| +--------------------------------------------------------+
| | 3. resolved theme colorScheme, fontScheme, |
| | background, dimensions from the |
| | theme record |
| +--------------------------------------------------------+
loses | 4. engine defaults e.g. spec/reference/ |
v | engine-defaults.json |
+--------------------------------------------------------+- Slide design —
slides[].design.* - Deck design —
design.*on the presentation root - Resolved theme — defaults carried by the theme record (
colorScheme,fontScheme,background,dimensions) - Engine defaults —
spec/reference/engine-defaults.json, compiled into core asENGINE_DEFAULT_THEME,ENGINE_DEFAULT_COLOR_SCHEMEandENGINE_DEFAULT_FONT_SCHEME
Resolution is per field, not per object. A slide that sets only design.contentAlignment inherits everything else from the deck design; a deck that sets only design.colorScheme keeps the theme's font scheme and background.
Two field-level rules complete the picture:
-
Base-plus-overrides within one object. Wherever a reference object carries an
id(ColorScheme,FontScheme, and the inlineAudience,PurposeandTone), theidresolves a catalog record as the base and sibling fields override the resolved record per key.design.themeis a reference string only. The string shorthand ("colorScheme": "cool-horizon") is equivalent to setting onlyid.text "colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" } catalog record "cool-horizon" sibling fields on the object accent1: "#2874A6" <-- replaced -- accent1: "#0F4C81" accent2: "#1B4F72" <-- kept light1: "#FFFFFF" <-- kept | v effective scheme: accent1 from the override, everything else from the record -
Explicit suppression.
watermark,header, andfooteracceptfalseto switch off an inherited value — distinct from omitting the field, which inherits.
Every reference inside this chain resolves the one way every content reference does: catalogs.custom, then the records embedded under catalogs.default, then the catalog the host registered for its source (name:id in catalogs.<name>, then the host catalog for its source); see how-opf-works.md and the catalogs page.
Worked example 1: color scheme through every level
{
"design": {
"theme": "classic",
"colorScheme": "forest-green"
},
"slides": [
{ "title": "Inherits the deck" },
{
"title": "Slide override",
"design": {
"colorScheme": { "id": "cool-horizon", "accent1": "#0F4C81" }
}
}
]
}- Slide 1: the
classictheme record supplies its own default color scheme, but the deck design setscolorSchemeexplicitly, soforest-greenwins (level 2 beats level 3). Fonts, background, and dimensions still come fromclassic. - Slide 2: slide design beats deck design (level 1 beats level 2). The
cool-horizonrecord resolves as the base, thenaccent1is replaced by#0F4C81. All othercool-horizonslots survive.
There is no ambiguity between "override" and "reference": every scheme value is a reference, and any sibling fields on the same object are overrides applied after the reference resolves.
Worked example 2: backgrounds and suppression
{
"design": {
"theme": "minimal",
"background": "light1",
"footer": {
"left": { "text": "Acme Corp" },
"right": { "text": "{{slide.number}}" }
}
},
"slides": [
{ "title": "Light slide on a theme that defaults to dark" },
{
"title": "Section divider",
"design": {
"background": {
"type": "gradient",
"gradient": {
"angle": 90,
"stops": [
{ "color": "#0B1B2B", "position": 0 },
{ "color": "#123A5F", "position": 1 }
]
}
},
"footer": false
}
}
]
}- Slide 1: the deck-level
background: "light1"overrides theminimaltheme's default background (dark2).light1is a theme slot — it resolves through the effective color scheme, which itself resolved through the chain above. - Slide 2: the gradient replaces the deck background for this slide only, and
footer: falsesuppresses the inherited footer rather than inheriting or replacing it.
Worked example 3: font scheme models
{
"design": {
"fontScheme": {
"id": "aptos",
"code": "JetBrains Mono"
}
}
}The aptos record supplies the OOXML pair (major/minor). The code role is an OPF-specific addition with no OOXML slot, so it layers on top without disturbing the pair. When serializing to PowerPoint, engines write major/minor to majorFont/minorFont and map abstract roles (heading, body) onto those slots. accent has no slot; the code family is written directly on code runs. The same slot-versus-role split applies to color schemes: OOXML slots (accent1–accent6, dark1/2, light1/2) round-trip directly, abstract roles (primary, text, surface, …) are mapped onto slots by the engine.
Code font
The code role resolves per key like every other override:
codeon the effectivedesign.fontSchemeobject;codeon the resolved font-scheme record (theconsolasandcourier-newrecords carry"Consolas"and"Courier New");- otherwise Roboto Mono, the documented fallback that
@openpresentation/opf-renderbundles.
The heading and body families are never reused as the code fallback, so choosing aptos still gives Roboto Mono code unless the deck sets code. resolveFontFamilies() in @openpresentation/opf/composition applies these rules for all engines.
Engine default font scheme
The last-resort font scheme applies only when neither the slide, the deck nor the resolved theme names one, or when the reference resolves nowhere. It is an engine default in code, not a catalog record: ENGINE_DEFAULT_FONT_SCHEME ({ major: "Aptos Display", minor: "Aptos", languageFamily: "latin" }, the drawing fields of the gallery's aptos scheme), recorded in engine-defaults.json and exported from the package root and @openpresentation/opf/composition. A document with no design uses the engine default theme (ENGINE_DEFAULT_THEME, which names no font scheme), so it gets Aptos in every engine without any catalog registered.
Every engine shares this one last resort, so a theme without fontScheme is measured, paginated, previewed and exported in the same fonts:
| Engine | Last resort | Where |
|---|---|---|
| Core pagination and validate | ENGINE_DEFAULT_FONT_SCHEME | packages/javascript/src/design-records.ts |
| opf-render preview | core's resolveSlideContext | src/svg.js |
| opf-editor composition and slide transfer | core's resolveSlideContext | src/font-defaults.js |
| opf-pptx export | core's resolveSlideContext | src/index.js |
Aptos is not openly licensed, so no OPF package bundles it. Previews take the same path for the last resort as for any aptos deck:
- Estimated layout (no
fonts): the SVG namesAptosandAptos Display, and the default raster engine draws them with its bundled sans-serif fallback (Roboto). - Measured layout with the opf-render office font pack (
loadFonts({pack: 'office'})):AptosandAptos Displayresolve to Intos and Intos Display, metric-compatible replacements from the OPF font policy (0.000% mean width difference against Aptos 2.01), and the substitution report lists both. With only the base pack andsubstitutionPolicy: "visual"they fall back to the visual alternates Roboto and Carlito. The PPTX always names Aptos. See font-fidelity.md. - Measured layout with only the base pack under the metric policy:
font-unavailablefor Aptos, as for a document with nodesign. Supply licensed Aptos faces, allow visual substitution, or set afallbackFamily.
packages/javascript/test/font-scheme-defaults.test.mjs checks the shared default in core pagination, and each sibling repository has a parity test.
References that resolve nowhere
A layout, theme, colour scheme or font scheme reference that neither the document embeds nor a registered catalog defines falls back, and no engine throws for it (unless the host asks for a strict export, strictReferences, which throws OPFUnresolvedReferenceError):
- Layout. A slide with no
layoutis composed with no layout record, and that is not a finding: title, subtitle, tag and content are arranged automatically, a slide with no body payload is a cover (the title block vertically centred) and the design hints apply. A layout reference that resolves nowhere composes the same way. An engine never substitutes a different layout. - Theme. A theme reference that resolves nowhere uses the engine default theme (
ENGINE_DEFAULT_THEME: the background and dimensions of the gallery'sminimal). - Colour scheme. A colour-scheme reference that resolves nowhere uses
ENGINE_DEFAULT_COLOR_SCHEME(the twelve slots ofcool-horizon). The theme's own colour scheme counts as the reference when the deck and slide name none, and resolves in the theme's catalog group first. An object without anidis laid over the engine default, so a partial inline scheme keeps the slots it does not name. - Font scheme. A font-scheme reference that resolves nowhere uses
ENGINE_DEFAULT_FONT_SCHEME. Sibling fields on an object reference still override it per key, so{ "id": "no-such-scheme", "major": "Inter", "minor": "Inter" }uses Inter, and acoderole still applies. An object withoutidis an inline scheme on the same base and reports nothing.
Each is one unresolved-reference diagnostic: { code, kind, reference, path, group, source?, fallback, message }, with path where the reference is written (slides.N.layout, slides.N.design.theme, design.colorScheme, design.fontScheme.id, or catalogs.<group>.themes.<id>.colorScheme for a reference inside an embedded theme), fallback automatic for a layout and engine-default otherwise, and a message that names the reference and the catalog source it was looked for in. validate reports the same references as opf/unresolved-reference warnings.
resolveSlideContext(presentation, index, { catalogs, fonts }) (package root) applies these rules with the slide, deck and theme precedence above. It returns the ComposeSlideOptions for one slide (canvas, layout, families as fontFamilies, measurement, slideNumber, slideCount), the slide to compose and draw (slide, with {{slide.number}}, {{slide.section}} and {{deck.slideCount}} substituted for those numbers), the diagnostics, and darkBackground (the preview's decision: the one colour the background names, resolved through the colour scheme and the deck's variables so var:brand, a scheme slot or primary/secondary/accent work, has a WCAG relative luminance below 0.179; a gradient counts as light, a picture reads light1, a pattern its backgroundColor; no opacity is applied), which composeSlide uses to pick logo variants. resolveDesignRecords and resolveFontScheme expose the same resolution for hosts that need only the records. Core pagination continues with the fallbacks and passes each diagnostic to onDiagnostic.
| Engine | Diagnostic channel | Reported |
|---|---|---|
| Core pagination | paginate(..., { onDiagnostic }) | once per path per call |
| opf-render preview | toSvg onDiagnostic | once per path per rendered slide |
| opf-editor | session.composeSlide / paginateSlide onDiagnostic option | once per call |
| opf-pptx export | toPptx(..., { onDiagnostic }) | once per path per export |
Every engine passes the catalogs its host registered (the catalogs option) to core, so a record a host registers resolves the same in preview, editor and export.
Color references in content
Content color fields (TextRun.color, styled table cell style.fill / style.color, table cell border color) accept references as well as literal hex, and those references resolve through the same chain above:
"color": "accent2" slot name -> effective color scheme slot
"color": "text" role name -> role-to-slot mapping, then the slot
"color": "var:risk" variable -> top-level variables map
"color": "#B42318" literal -> used as-is (frozen at authoring time)-
Slot names (
accent1–accent6,dark1,dark2,light1,light2,hyperlink,followedHyperlink) read the named slot from the effective color scheme — the one produced by the slide → deck → theme → engine-default precedence at the top of this page. A slide-leveldesign.colorSchemeoverride therefore recolors that slide's named runs too. -
Role names (
primary,secondary,accent,background,surface,text,textSecondary) resolve through one shared definition,resolveColorRoles(), which the opf-render preview, the PPTX export andvalidateall call, so the same deck draws the same colors in each. A role defined on the effective scheme wins; otherwise it defaults from a slot:Role Default (through the slide's variant; see Color variants) primary,secondary,accentthe first three accents of the variant: accent1,accent2,accent3unlessvariant.accentsreorders thembackgroundthe slide's own resolved background when it is one color (solid or pattern background color); else the variant's bg1(light1by default, the scheme'sbackgroundrole overriding it there). A gradient or picture background uses the variant, or itstone, orlight1.surfacebg2of the variant:light2on a light slide,dark2on a dark onetexttx1of the variant:dark1, orlight1on a dark slide. Atextoverride applies on a light slide only, so a dark slide always keeps readable light text.textSecondarytx2of the variant:dark2, orlight2on a dark slideA slide is dark when its background's WCAG relative luminance is under 0.179, the point where white and black text contrast equally.
-
Links. A link run (
link) with nocolorof its own is drawn underlined in the scheme'shyperlinkslot (the OOXMLhlinkcolor; Office blue#0563C1when the scheme sets none), in the preview and in the PPTX export (a:schemeClr hlinkwhere the deck theme holds that color). Where that color has under 4.5:1 contrast against the slide background (the default Office blue on a dark slide), the slide'stextcolor is used instead, as the slide tag does for a low-contrast primary color. A link run with its owncolorkeeps it, and ahyperlinkColorRef always names the slot.followedHyperlinkis written to the PPTX theme; a static preview cannot know which links were visited, so it draws every link inhyperlink. -
Variable references (
var:<id>) resolve against the document's top-levelvariablesmap, independent of the scheme. Variables are deck-scoped named colors — use them for values that have meaning (var:risk) or repeat across slides. An unknown id is a validation warning, never an error, and engines fall back to their default text color.
The styled table cell and border color fields enforce the reference forms at the schema level (a typo like "acent2" is a schema error there — neither hex, a known name, nor a var: reference). Run colors stay open strings so imported decks keep validating: an unrecognized run color is a validation warning, and renderers fall back to the theme text color — the same warn-don't-error posture unknown catalog ids get. Unknown var: ids are warnings everywhere.
Color variants
A deck has one color scheme of twelve slots. A variant is a mapping over those same slots, as PowerPoint's clrMap is, never a second scheme: which slot is the background, and so which are the text and the alternate surface, plus an optional order for the accents. Everything OPF draws refers to the roles, so a slide that changes variant recolors text, lines, cards, chart furniture and code together.
variant is a string, the same way colorScheme is: the slot that is the background.
{
"design": { "variant": "dark1" },
"slides": [{ "title": "Light one", "design": { "variant": "light1" } }]
}"dark1" is { "background": "dark1" }. The object form is only needed to reorder the accents: { "background": "light1", "accents": ["accent2", "accent1", "accent3", "accent4", "accent5", "accent6"] }. Both forms are allowed on design and on slides[n].design, and what resolveColorRoles and resolveSlideContext return is always the normalized object (normalizeColorVariant).
variant (or variant.background) | bg1 | tx1 | bg2 | tx2 |
|---|---|---|---|---|
light1 (default) | light1 | dark1 | light2 | dark2 |
dark1 | dark1 | light1 | dark2 | light2 |
light2 | light2 | dark1 | light2 | dark2 |
dark2 | dark2 | light1 | dark2 | light2 |
(light2 and dark2 keep the background slot as the surface too, as a slide with that theme background has always been drawn, so the engine default theme, whose background is dark2, is unchanged.) variant.accents (object form) is a permutation of accent1 to accent6; entry i is the slot drawn as accent i + 1, which reorders primary, secondary, accent and the chart series colors.
Which variant a slide has, from the most specific to the least (slide design, deck design, then the theme record), at the first level that says anything:
- a
variant; - a theme-slot background (
"dark1",{ "type": "theme", "slot": "dark1" }), which is the variant of that slot, sodesign.backgroundstays a shorthand; - a picture or gradient background with a
tone("dark"or"light"), which is thedark1orlight1variant. Without a tone only a variant of a less specific level can apply; with none the slide counts as light, as before; - any other background (a solid or pattern color) decides by luminance (dark below 0.179), as before.
A theme record can list the variants it offers (variants, from gallery 4): named values of variant, such as light, light-alt, dark and dark-alt on minimal, classic and bold. They are for pickers: choosing one writes its variant into design.variant (or a slide's), and the document keeps that value, not the entry id. The engines read only design.variant. There is no dark theme in the default catalog: a dark deck is a theme with a dark variant, for example { "theme": "classic", "variant": "dark1" }.
A slide's variant replaces the deck's whole, accent order included. resolveSlideContext(...).resolved.roles is the resolved record, and resolveColorRoles(scheme, { variant, tone, background }) is the function: besides the colors it returns variant, map (bg1, tx1, bg2, tx2 and the accents; clrMapAttributes(map) gives the PowerPoint clrMap attributes for the master and for a slide's clrMapOvr), accents (the six colors in order) and neutrals.
Scheme-derived neutrals
Lines and quiet colors are not fixed grey. roles.neutrals derives each from the map, as PowerPoint's chart styles do: the text color (tx1) at a share over the background (bg1), chosen as a DrawingML luminance transform of the slot so a PPTX can write <a:schemeClr val="tx1"><a:lumMod val="15000"/><a:lumOff val="85000"/></a:schemeClr> and the preview draws the same color. Each neutral is { slot, hex, lumMod?, lumOff?, alpha? } (the transforms in thousandths of a percent; hex is the transform applied, applyLuminance(slotHex, lumMod, lumOff)).
| Neutral | Derivation |
|---|---|
border (also roles.border) | tx1 at 20% over bg1: card, rule and table borders |
gridline | tx1 at 15%: chart gridlines |
axisLine | tx1 at 25%: chart axis lines |
chartText | tx1 at 65%: chart axis labels and legend text |
outline | bg1: pie and treemap outlines, the video play icon, anything drawn on a mark |
trendNeutral | tx2: the neutral metric trend |
codePanel | tx1 on a light map, bg2 on a dark one: the code block is a dark card on either |
codeText | bg1 or tx1, whichever reads better on the panel |
codeComment | the code text at 65% over the panel (comments and line numbers) |
codeSyntaxPaletteForRoles(roles) builds the syntax palette from codePanel, codeText, codeComment and the variant's first three accents, and codeHighlightColors(scheme, palette, codePanel.hex) takes the panel the highlight band sits on. readableSchemeColor(fill, roles) returns bg1 or tx1 (as a scheme color), whichever reads on a fill the scheme does not name, for table header text and text on marks. The validator's contrast rules read the same roles, so a dark variant is checked against its own text and surface.
Background colors are ColorRefs
The colors of a solid background (SolidBackground.color), of each gradient stop and of a pattern (foregroundColor, backgroundColor) take the same forms as the content fields above: a literal hex, a slot or role name, or a var:<id> reference. The schema keeps these fields as plain strings, so a reference validates; engines resolve it through resolveColorRef() against the effective color scheme and the deck variables, and a reference that resolves to nothing is drawn as the engine default (white for a background), as for any other unresolvable color. The default text color of the slide follows the resolved background. A PPTX export writes a:schemeClr where the deck theme holds the named slot or role exactly, and the resolved literal otherwise (FF-24 conventions).
Decision, 2026-09-30 (agent decision, vetoable). The spec coverage audit found that
SolidBackground.coloracceptedvar:and slot names (the field is a string) but both engines painted white, whilecontent-item-design-overrides.mdalready states that every color field must accept the ColorRef forms and never hex alone. Rather than tighten the schema (which would break documents that validate today), the engines resolve ColorRefs in backgrounds. The owner can veto this by restricting the three background color fields toHexColorin the schema and the engines to hex only.
@openpresentation/opf/composition exports resolveColorRef() with the shared slot, role, variable, and hex rules above so renderers and exporters do not drift. Pass the effective color scheme, optional resolved role colors, the deck variables map, and a theme-text fallback for unrecognized references.
Script fonts and language
OOXML gives each theme font (major and minor) three script slots: latin, East Asian (ea) and complex script (cs). The presentation language and the effective font scheme resolve to all three:
latin design font scheme heading/body (the chain above)
eastAsian 1. design.fontScheme.eastAsian explicit slot
complexScript 2. the scheme's own major/minor when languageFamily is ea / cs (or eastAsian /
complexScript, the same values) and its
languages list (language ids) is empty or
names the language
3. the language's font scheme when the language's script uses the slot
4. the latin family otherwise- A language record's
script(ISO 15924) picks its slot. East Asian scripts (Jpan,Hans,Hant,Kore, ...) useeastAsian. Complex scripts (Arab,Hebr,Deva,Thai, ...) usecomplexScript. Latin, Cyrillic, Greek and other scripts uselatin.directiondefaults from the script (Arabic and Hebrew are right-to-left). - The language's
fontSchemeapplies to PowerPoint output andgoogleFontSchemeto Google Slides output. For Latin-script languages, the design font scheme always supplies the latin slot. - A language sets
lang, the text direction and the script slots, and never the Latin scheme: onlydesign.fontScheme(slide, then deck, then theme, then the shared defaultaptos) sets the latin fonts, and a language record'sfontSchemeis a default for its own script slot, not a deck font. The PPTX theme'sa:eaanda:csare written only for a slot a script font is selected for (the scheme's explicit slot or the language's script font) and stay empty otherwise, as in Office's own themes. See Language contract and Theme slots. - A Latin deck therefore repeats its heading/body family in
ea/cs. A Japanese deck withdesign.fontScheme: { "major": "Carlito", "minor": "Carlito" }keeps the Latin family inlatinand uses Meiryo (PowerPoint) or Noto Sans JP (Google Slides) inea.design.fontScheme.eastAsian/.complexScript({ "major": ..., "minor": ... }) name a script font explicitly, for example for CJK text inside a Latin deck.
@openpresentation/opf/composition exports resolveScriptFonts(document, { app, slideIndex }) (app is "powerpoint", the default, or "google-slides"), and the package root exports normalizeLanguageFamily(value) (which reads a font scheme's languageFamily, eastAsian as ea and complexScript as cs). resolveScriptFonts returns the heading and body slots, the OOXML lang (a curated ooxmlLang culture tag such as ja-JP or ms-MY, or an authored region tag), the canonical bcp47 tag, script, direction/rtl, and the per-script supplemental theme font. Renderers and exporters should use it rather than re-deriving slots. The model, the OOXML mapping and the open questions are in programs/font-fidelity-everywhere/script-font-model.md. opf-render and opf-pptx implement it (FF-07, FF-19, FF-49).
Brand assets and layout hints
The 2026-09-30 spec coverage audit found that the deck logo, organization.logo, speaker.photo, design.contentDirection, design.chartPrimary, design.listBullet and fontScheme.accent validated, edited and round-tripped but changed nothing in any engine. This section states what they do now. Every rule below is implemented once, in composeSlide() and resolveLogo() of @openpresentation/opf, and consumed by the renderer and the exporter; the decisions marked (vetoable) are agent decisions the owner can overturn.
Layout templates and design
An OPF layout record (opf-layout/v2) is one ordered list of regions, declared once, in reading order, with a direction (row or column) and splits for two dimensions. A region has a name, a relative size and rules: accepted content kinds, role, flow, capacity and optional bleed. layoutContent(record) and layoutStructure(record) summarize those regions for hosts; composeSlide() reports their binding and boxes in geometry.regions. A slide may use layout: "auto" to bypass a record explicitly. Layout templates documents the binding and flow rules.
The effective design value of each key comes from the slide, then the deck, then the resolved theme, then the layout record, then the engine default. This includes titleAlignment, contentAlignment, contentBox, contentDirection, chartPrimary, imageFit, listBullet and mirror. resolveDesignHints() reports the supplying level, and SlideComposition.design holds the merged result. A layout's regions determine placement; design.mirror reverses its tracks, while the slide's own composition arranges the first primary flowing region. A top-level image block may still carry its own placement band. A layout sits on any slide background; it does not opt into a picture.
The tag (eyebrow) follows titleAlignment on every slide, and is left-aligned when none resolves, like the title, so a layout that centres its content does not centre the tag over a left-aligned title. On a cover the subtitle joins them. Only the slide's own design.contentAlignment keeps the tag (and a cover's subtitle) on content alignment, preserving the 2026-10-06 authoring decision.
Logos live on the organization (RR-71, OPF 0.18)
A logo belongs to an organization. Organization.logo is one path or Asset for every shape, or up to four shapes:
"organization": {
"id": "acme", "name": "Acme",
"logo": {
"full": { "onLight": "./assets/acme-logo.svg", "onDark": "./assets/acme-logo-white.svg" },
"icon": "./assets/acme-mark.svg"
}
}resolveLogo(presentation, slide, { shape, onDark, slideIndex, reference }) (@openpresentation/opf/composition) returns { source, path, shape, variant, reference, designPath? } or null:
organization 1. options.reference var:organization.beta.logo.icon
2. slides[i].design.logo, then design.logo:
false draws no logo; a reference picks the organization
(and the shape, when it names one)
3. the primary organization role "primary", else the first
shape the reference's shape, else options.shape (default full)
missing shape -> full -> first of wordmark, stacked, icon
variant onDark on a dark background, else onLight; a missing one uses
the other; a plain asset is "default"
path organization.logo, organization.1.logo.icon,
organization.logo.full.onDark, ...An override that names an organization without a logo draws nothing: it never falls back to another organization. Hosts pass their own background luminance test as composeSlide(..., { darkBackground }) (resolveSlideContext computes it); core never inspects colors.
The same logos are placed anywhere through slide-scoped built-in references (var:organization.logo, var:organization.logo.icon, var:organization.beta.logo.wordmark), which resolve for each slide's background: see templates and variables.
Where the logo is drawn (vetoable)
- Cover and section slides. A slide with no body payload on a title-only template, after empty template regions collapse, or without a layout (the same rule that centers covers) draws the primary organization's
fulllogo (or thedesign.logooverride) at the top-left of the free area, inside the slide padding and below any header furniture.composeSlidereturns it asgeometry.logo({ box, shape, path, source, variant, reference, anchor: 'left' }):x = area.left + padding,yat the image-safe heading top,height = 56reference pixels at a 720-pixel short edge,width = min(4 * height, free width). Headings start one gap below the box and the cover-centering rule centers the tag/title/subtitle group in the remaining span; the logo itself does not move. Consumers fit the image inside the box preserving its aspect ratio, anchored left and vertically centered (SVGpreserveAspectRatio="xMinYMid meet"; PPTX computes the fitted size from the raster dimensions and places it atbox.x). Nothing is drawn when no logo resolves ordesign.logoisfalse. Content slides never get an automatic logo (vetoable: it would move every content area). - Headers and footers. A zone places a logo like any picture:
"image": "var:organization.logo.icon".layoutFurnitureresolves the reference for the slide's background; the image part carries the asset inimage, the organization path insourcePathand the reference inreference. Without a logo the engine reportsunresolved-contentat<zone>.image. The zone's parts sit in one row (see dynamic composition). - Picture bullets. See
listBulletbelow: theiconshape.
Decision, 2026-09-30 (agent decision, vetoable). 84 of the 126 bundled example decks carry a logo, so 81 cover slides gain a logo and their heading group moves down. The placement (top-left, 56 px, full logo) and the content-slide exclusion are the reference-engine defaults; a layout-driven logo slot is a separate design.
speaker.photo is a built-in image variable (vetoable)
No reference engine draws a speaker photo by itself: the schema has no speaker slot on any slide and no slide-to-speaker link, and a speaker block on covers would be a separate design. The author places it explicitly with the built-in image variable ("image": "var:speaker.photo", see templates and variables); the field also round-trips through PPTX provenance.
contentDirection
The effective value follows the normal design precedence: slide, deck, theme, layout, engine default. On automatic slides vertical sets the root arrangement to column and horizontal to row, unless the slide explicitly sets composition.mode. Promoted regions keep their geometry and nested content groups keep their own composition.
On a v2 template, contentDirection changes every flowing region into a row or column without changing the template's regions. An atomic flow: "none" region remains atomic. Without the direction hint each region uses its declared flow. Layout records do not have composition.mode, columns or weights; their region list defines the grid.
titleAlignment and contentAlignment in right-to-left decks
Decision, 2026-10-01 (RR-05, supervisor decision, vetoable). Alignment is logical for right-to-left text. In a deck whose language is written right to left,
leftmeans the start edge andrightthe end edge of each paragraph: an Arabic or Hebrew paragraph with the default (or authored)leftis drawn against the right edge, with its bullets, indents and table cells to match, while an English paragraph in the same deck keeps the left edge.centeris unchanged and a left-to-right deck is unchanged. The effective value (slide design, then deck design, thenleft) is still whatitem.alignmentreports;placement.lines[i].alignmentand the exportedalgncarry the physical edge. A right-to-left deck that wants a paragraph at its end edge setsright. See Layout direction.
chartPrimary
The resolved design.chartPrimary applies on automatic slides only. It changes the root arrangement when the slide has no promoted regions, no composition.mode of its own, and its root nodes contain at least one chart leaf and at least one node that is not a chart. The first chart node is primary and the other root nodes form one synthetic sub-grid:
left/right: the root is a two-track row with weights[3, 2](chart first forleft, last forright); the rest arrange inautomode inside their track.top/bottom: a two-track column with weights[3, 2](chart first fortop).none, or any other value: no change, the existing automatic grid with equal weight.
Automatic composition builds the primary chart track and a direct content group for the other root nodes, preserving the 3:2 chart weight and source content order. A v2 template remains authoritative and does not apply this automatic hint. OPF 0.19 removes the v1 record-construction helper chartPrimaryLayout; authors use chart-beside or a custom v2 template for an authored grid.
The synthetic container has no OPF path, so it records no groups, flows or explanation entry; the root decision has reason: 'chart-primary' and selectedColumns 2 (row) or 1 (column). Explicit root columns and weights are ignored while the automatic hint applies. A chart inside a nested group, a chart-only root, or a single root chart payload leaves the arrangement unchanged. A slide using a v2 template follows that template instead.
listBullet (vetoable)
character (the default) draws the current glyph marker. image draws the organization's icon logo (resolveLogo(..., { shape: 'icon', onDark }): the primary organization's, or the organization and shape design.logo names) as a picture bullet: every items/bullets item in composeSlide and every listEntries[] entry of its fit carry bulletImage: { source, path }. Marker geometry is unchanged, and each entry carries bulletBox, where the image draws: a square of side marker.fontSize * PICTURE_BULLET_SCALE (0.65, exported) whose bottom sits on the marker baseline (marker.y) with its left edge at marker.x. The scale is what desktop PowerPoint draws for an a:buBlip at a:buSzPct 100000 (what the exporter writes), measured as a square 10, 15, 16, 20 and 31 px wide at font sizes of 16, 24, 25, 32 and 48 px (0.625 to 0.646; the heights run a pixel more from anti-aliasing) with its bottom on the text baseline, in Arial, Aptos, Georgia and Courier New alike, so it does not depend on the typeface; the text start and hanging indent are identical in both. The exporter sets no size, because PowerPoint sizes the bullet itself; consumers that draw it themselves (the preview) use bulletBox. Vetoable: the constant is a measurement, not a rule of the format. When image is set and no logo resolves, the glyph stays and the slide reports one unresolved-content diagnostic at slides.N.design.listBullet or design.listBullet, only when the slide has a list. The renderer draws an <image> per marker; the exporter writes native picture bullets (a:buBlip).
fontScheme.accent
resolveFontFamilies() returns accent only when the effective scheme defines an accent role (a family string or a Font object). The slide tag (eyebrow) and the quote body use fonts.accent ?? <current family> (body for the tag, heading for the quote body); nothing else changes. The renderer loads and embeds it, the exporter writes it on those runs (a:latin) while the theme fonts stay major/minor, and import keeps restoring design.fontScheme from provenance. None of the bundled examples sets an accent font.
What is not part of this chain
Beyond the color references above, content payloads carry no design controls in v1 — position, fontSize overrides at payload level, and the like were deliberately kept out while the content model stabilizes (see content-item-design-overrides.md; styled table cells and rich-text runs carry the only per-content styling, and their color fields take the reference forms above). The design system, plus layout hints (titleAlignment, contentBox, chartPrimary, ...) and dynamic composition, is the styling surface of an OPF document.