# slides@0.1

**Status:** normative. **Inherits:** [`../conventions.md`](../conventions.md). **Schema:** [`schema.json`](schema.json). **Fixtures:** [`fixtures/`](fixtures/). **Owns diagnostic codes:** `MDTO1400`–`MDTO1499`.

This is a content model, not the shared task item model. It does not reinterpret task checkbox state.

**Companion skill:** [Authoring and editorial guidance](https://markdownto.ai/specs/slides.skill.md) (non-normative).

## 1. Purpose

`slides@0.1` describes a portable presentation: level-2 headings are slides, Markdown is the visible content, and sparse direction chooses a constrained layout and speaker notes.
The file stays readable, diffable, and authoritative in any Markdown viewer.

---

## 2. Grammar

### 2.1 Envelope

```yaml
---
markdownto: slides@0.1
title: A better beginning
---
```

Shared envelope and preservation rules apply. Unknown keys are preserved and inert. Settings are validated without network access.

### 2.2 Slides and direction

One optional leading `#` is the title. Each `##` starts a slide; titles are nonempty and case-insensitively unique. Lower headings are content. Content before the first slide is invalid.

A first paragraph consisting entirely of inline fields is direction. `[layout:: standard]` is the default. Unknown fields are preserved and inert; repeated fields are invalid. No arbitrary CSS, JavaScript, or HTML layouts are executed.

| Layout | Composition and required content |
| --- | --- |
| `title` | Large heading and a short subtitle. |
| `standard` | Prose, bullets, tables, images, and code. |
| `split` | Two columns separated by exactly one thematic break (`---`). |
| `quote` | A prominent blockquote, with optional attribution. Requires a blockquote. |
| `image` | One prominent picture with optional caption. Requires a visible Markdown image. |
| `section` | A chapter divider with a large heading and short introduction. |
| `three-column` | Three parallel columns, separated by exactly two thematic breaks. |
| `image-left` | Picture left, explanatory text right. |
| `image-right` | Explanatory text left, picture right. |
| `video` | A bounded YouTube player and short supporting text. Requires a `youtube` field. |

Picture-and-text layouts require exactly one image in its own paragraph; the renderer places that paragraph in the picture column. Images use ordinary `![description](relative/path.png)` syntax and need meaningful alternate text. Pictures preserve their proportions rather than being silently cropped. Local PNG, JPEG, and GIF assets are embedded by `slides present` and `slides build`, so moving the resulting HTML or PDF does not break them. Local builds refuse missing, remote, or out-of-directory assets; they do not fetch them. Paths are relative to the Markdown file. When using the website editor, image URLs resolve against its hosting page; arbitrary files on the user's machine are not uploaded or accessible implicitly.

In a video layout, the first standalone Markdown image, when present, becomes the poster inside the player area. It preserves its proportions, is bundled like other local images, and reappears when playback closes. Other content remains supporting prose. No thumbnail is fetched automatically.

A `### Notes` heading starts speaker notes for the rest of that slide. Notes are available in the notes panel and source but excluded from audience slides and PDF. Notes are not secret: they remain in the exported HTML. Images only in notes do not satisfy an audience image layout.

#### Appearance

`theme` is `paper` (default), `ink`, or `sand`. `aspect` is the string `16:9` (default) or `4:3`. Both are exact, case-sensitive enums.

The following optional settings work in frontmatter for the whole deck, or as slide direction overriding that default:

| Setting | Allowed values |
| --- | --- |
| `background` | A six-digit hex color such as `#20352b`. Quote hex colors in YAML. |
| `color` | A six-digit hex color for slide text. |
| `accent` | A six-digit hex color for emphasized text and links. |
| `font` | `editorial` (serif headings, sans body), `serif`, or `sans`. Uses dependable system fonts. |
| `font-size` | A finite number from 20 to 40, in CSS pixels on a 1280-wide canvas. Default 26.24. |

Body text scales with the canvas. Headings, captions, table/code text, and interface controls retain their layout-specific hierarchy; `font-size` does not resize every element. Colors and fonts apply to the slide, not the surrounding interface or YouTube's player. Both main text and accent must have WCAG relative-luminance contrast of at least 4.5:1 against the background. Appearance is authored in Markdown; there is no freeform CSS or drag-to-resize editor.

#### YouTube

Use `[layout:: video] [youtube:: https://www.youtube.com/watch?v=M7lc1UVf-VE]`. `youtube` accepts an 11-character video ID or HTTPS YouTube watch, embed, shorts, or youtu.be URLs. Only exact supported YouTube hosts are accepted. Optional `start` or `t` URL parameters preserve an integer start time (seconds or `1h2m3s`), up to 86400 seconds. Other URL parameters never become player permissions or autoplay settings. The field is invalid outside a video layout.

The renderer initially shows a local placeholder; it must not fetch thumbnails or contact YouTube before an explicit Load action. It then uses a titled, privacy-enhanced `youtube-nocookie.com` iframe with controls and no autoplay. Navigating away, entering overview, printing, or hiding the page removes the player, stopping background playback. No YouTube API key is required. The player must have at least a 200 × 200 CSS-pixel viewport; smaller canvases keep the external link instead.

Embedded playback needs internet access, a hosted HTTP(S) page, and a video whose owner permits embedding. YouTube may refuse local files, sandboxed hosts, restricted videos, or blocked requests. The website may host the player outside its isolated source frame through a bridge that validates the sender, authored video, and in-frame bounds; arbitrary embedded hosts need equivalent support. An Open on YouTube link must remain available at all times; local files explain that fallback. PDF contains a readable video link, not a playable video. Packaging never downloads the video.

#### Fit and navigation

The implementation measures all slides at the same reference canvas (1280 × 720 or 1280 × 960), after fonts and images load. The live view and PDF gate share checks for bottom/footer overlap, content wider than its column/canvas, and missing images. Preview lists failing slide numbers; its Print/PDF button and CLI PDF build refuse those failures. This detects overflow; it does not optimize editorial density or guarantee legibility on every physical screen. Authors shorten content, choose another layout, or split the slide. The renderer must not silently shrink text to make an overloaded slide fit.

PDF uses the same compositions, one page per slide with no interface furniture. A browser's own print shortcut can bypass the application's preflight; the checked delivery path is `mdto slides build`. The renderer honors reduced motion and exposes keyboard navigation. Overview, selected slide, notes visibility, and fullscreen are presentation state only.

### 2.3 Canonical IR

The IR sits at `result.slides`, beside `result.document`. It carries the envelope, ordered source-positioned content, parsed direction, effective settings, and domain data defined in `schema.json`. Source offsets address edits, never an independently parsed renderer copy. Parse and serialize reproduce the source byte for byte.

### 2.4 Diagnostics

| Code | Severity | Meaning |
| --- | --- | --- |
| `MDTO1400` | error | No sections. |
| `MDTO1401` | error | Invalid heading or content before a section. |
| `MDTO1402` | error | Invalid settings or direction. |
| `MDTO1403` | error | Invalid required content structure. |
| `MDTO1405` | error | Duplicate section or option identity. |

## 3. Rationale

**One heading axis.** `##` has one domain meaning, so authoring, validation, rendering and operations agree. Familiar Markdown remains useful without this runtime.

**Explicit state.** Meaningful user input belongs in the file; selected views and transient controls do not. Renderers never invent missing facts. Shared safe rendering treats executable-looking content as data.

**Small portable surface.** This format chooses bounded semantics over arbitrary executable extensions. Its companion skill teaches editorial judgment without making taste a validation rule.

## 4. Examples

````markdown
---
markdownto: slides@0.1
title: A better beginning
---

## A better beginning

[layout:: title]

Every great project starts with a clear question.
````

See the valid fixtures for a complete, realistic example and edge cases. Invalid fixtures define the exact errors, not examples to imitate.

## 5. Verb reference

`slides@0.1` owns the vocabulary **present, build, images**.

| Verb | Signature | Source edit |
| --- | --- | --- |
| `present` | `mdto slides present [--out deck.html]` | Build the standalone presentation; no source edit. |
| `build` | `mdto slides build [--out deck.pdf] [--browser path]` | Build one PDF page per slide; notes are excluded. |
| `images` | `mdto slides images [--out directory] [--slide number] [--browser path]` | Export numbered PNGs and a JSON review manifest; no source edit. |

Images use the same slide renderer at 1280×720 (16:9) or 1280×960 (4:3), excluding navigation and notes. `--slide` is a one-based slide number; omitted means all slides. Default output is the source path without `.md`, plus `.images`. The output directory must not exist. Captures and `manifest.json` are published together after all requested images complete; browser failures leave no partial output directory. Browser lookup is `--browser`, `MDTO_CHROME`, then common Chromium installations. The manifest and `--json` response include absolute image paths, slide numbers and titles, canvas dimensions, source hash, and per-slide `issues` arrays from the shared fit check. Fit issues are reported without suppressing inspection images or failing the command; agents must review these before delivery. Images show the fixed canvas, so overflowing content may be clipped. YouTube appears as its static link fallback, never a playing video. Local pictures are embedded; missing or unsupported local assets refuse export.

### Common flags and editing guarantees

`--file <path>` selects the source; `--json` uses the `mdtoJson: 1` envelope; `--help` describes the verb. Mutation verbs also accept `--expect <hash>` and `--dry-run`; they validate the complete batch and refuse stale or invalid results without writing. Question/criterion references are one-based numbers or exact case-insensitive titles. Updated fences use JSON escaping and preserve all untouched bytes. Repeating an already-satisfied mutation is a no-op. Builds accept `--out`; outputs never replace the source. Cross-spec conversion is refused when meaning would have to be invented; use explicit authoring instead.
