@aivorynet/slaide 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +201 -0
  3. package/README.md +87 -0
  4. package/bin/slaide.js +25 -0
  5. package/dist/assets.d.ts +10 -0
  6. package/dist/assets.js +54 -0
  7. package/dist/assets.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +433 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/compare/index.d.ts +24 -0
  12. package/dist/compare/index.js +246 -0
  13. package/dist/compare/index.js.map +1 -0
  14. package/dist/compare/ssim.d.ts +2 -0
  15. package/dist/compare/ssim.js +46 -0
  16. package/dist/compare/ssim.js.map +1 -0
  17. package/dist/compiler/chrome.d.ts +7 -0
  18. package/dist/compiler/chrome.js +94 -0
  19. package/dist/compiler/chrome.js.map +1 -0
  20. package/dist/compiler/compile.d.ts +2 -0
  21. package/dist/compiler/compile.js +432 -0
  22. package/dist/compiler/compile.js.map +1 -0
  23. package/dist/compiler/markdown.d.ts +26 -0
  24. package/dist/compiler/markdown.js +439 -0
  25. package/dist/compiler/markdown.js.map +1 -0
  26. package/dist/compiler/tokens.d.ts +13 -0
  27. package/dist/compiler/tokens.js +127 -0
  28. package/dist/compiler/tokens.js.map +1 -0
  29. package/dist/container.d.ts +47 -0
  30. package/dist/container.js +309 -0
  31. package/dist/container.js.map +1 -0
  32. package/dist/engine-entry.d.ts +1 -0
  33. package/dist/engine-entry.js +71 -0
  34. package/dist/engine-entry.js.map +1 -0
  35. package/dist/export-keynote/keynote.d.ts +10 -0
  36. package/dist/export-keynote/keynote.js +58 -0
  37. package/dist/export-keynote/keynote.js.map +1 -0
  38. package/dist/export-pptx/embed-fonts.d.ts +5 -0
  39. package/dist/export-pptx/embed-fonts.js +159 -0
  40. package/dist/export-pptx/embed-fonts.js.map +1 -0
  41. package/dist/export-pptx/inject-anim.d.ts +28 -0
  42. package/dist/export-pptx/inject-anim.js +150 -0
  43. package/dist/export-pptx/inject-anim.js.map +1 -0
  44. package/dist/export-pptx/pptx.d.ts +10 -0
  45. package/dist/export-pptx/pptx.js +425 -0
  46. package/dist/export-pptx/pptx.js.map +1 -0
  47. package/dist/export-pptx/transitions.d.ts +16 -0
  48. package/dist/export-pptx/transitions.js +82 -0
  49. package/dist/export-pptx/transitions.js.map +1 -0
  50. package/dist/import/emit.d.ts +5 -0
  51. package/dist/import/emit.js +274 -0
  52. package/dist/import/emit.js.map +1 -0
  53. package/dist/import/index.d.ts +21 -0
  54. package/dist/import/index.js +192 -0
  55. package/dist/import/index.js.map +1 -0
  56. package/dist/import/pptx.d.ts +117 -0
  57. package/dist/import/pptx.js +1043 -0
  58. package/dist/import/pptx.js.map +1 -0
  59. package/dist/import/raster-com.d.ts +27 -0
  60. package/dist/import/raster-com.js +113 -0
  61. package/dist/import/raster-com.js.map +1 -0
  62. package/dist/import/raster-extension.d.ts +35 -0
  63. package/dist/import/raster-extension.js +27 -0
  64. package/dist/import/raster-extension.js.map +1 -0
  65. package/dist/index.d.ts +63 -0
  66. package/dist/index.js +174 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/install/detect.d.ts +17 -0
  69. package/dist/install/detect.js +82 -0
  70. package/dist/install/detect.js.map +1 -0
  71. package/dist/install/index.d.ts +20 -0
  72. package/dist/install/index.js +126 -0
  73. package/dist/install/index.js.map +1 -0
  74. package/dist/install/registry.d.ts +33 -0
  75. package/dist/install/registry.js +169 -0
  76. package/dist/install/registry.js.map +1 -0
  77. package/dist/install/skills.d.ts +17 -0
  78. package/dist/install/skills.js +72 -0
  79. package/dist/install/skills.js.map +1 -0
  80. package/dist/master-io.d.ts +7 -0
  81. package/dist/master-io.js +55 -0
  82. package/dist/master-io.js.map +1 -0
  83. package/dist/mcp/server.d.ts +3 -0
  84. package/dist/mcp/server.js +114 -0
  85. package/dist/mcp/server.js.map +1 -0
  86. package/dist/optimize/export-optimize.d.ts +21 -0
  87. package/dist/optimize/export-optimize.js +167 -0
  88. package/dist/optimize/export-optimize.js.map +1 -0
  89. package/dist/parser/frontmatter.d.ts +8 -0
  90. package/dist/parser/frontmatter.js +45 -0
  91. package/dist/parser/frontmatter.js.map +1 -0
  92. package/dist/parser/parse.d.ts +2 -0
  93. package/dist/parser/parse.js +184 -0
  94. package/dist/parser/parse.js.map +1 -0
  95. package/dist/render/anim.d.ts +50 -0
  96. package/dist/render/anim.js +213 -0
  97. package/dist/render/anim.js.map +1 -0
  98. package/dist/render/charts.d.ts +3 -0
  99. package/dist/render/charts.js +171 -0
  100. package/dist/render/charts.js.map +1 -0
  101. package/dist/render/css.d.ts +3 -0
  102. package/dist/render/css.js +183 -0
  103. package/dist/render/css.js.map +1 -0
  104. package/dist/render/echarts-lib.generated.d.ts +1 -0
  105. package/dist/render/echarts-lib.generated.js +7 -0
  106. package/dist/render/echarts-lib.generated.js.map +1 -0
  107. package/dist/render/html.d.ts +5 -0
  108. package/dist/render/html.js +186 -0
  109. package/dist/render/html.js.map +1 -0
  110. package/dist/render/inject.d.ts +12 -0
  111. package/dist/render/inject.js +19 -0
  112. package/dist/render/inject.js.map +1 -0
  113. package/dist/render/mermaid-lib.generated.d.ts +1 -0
  114. package/dist/render/mermaid-lib.generated.js +7 -0
  115. package/dist/render/mermaid-lib.generated.js.map +1 -0
  116. package/dist/render/runtime.d.ts +1 -0
  117. package/dist/render/runtime.js +393 -0
  118. package/dist/render/runtime.js.map +1 -0
  119. package/dist/render-pdf/pdf.d.ts +9 -0
  120. package/dist/render-pdf/pdf.js +53 -0
  121. package/dist/render-pdf/pdf.js.map +1 -0
  122. package/dist/render-png/montage.d.ts +14 -0
  123. package/dist/render-png/montage.js +75 -0
  124. package/dist/render-png/montage.js.map +1 -0
  125. package/dist/render-png/shoot.d.ts +25 -0
  126. package/dist/render-png/shoot.js +79 -0
  127. package/dist/render-png/shoot.js.map +1 -0
  128. package/dist/scaffold.d.ts +9 -0
  129. package/dist/scaffold.js +36 -0
  130. package/dist/scaffold.js.map +1 -0
  131. package/dist/types.d.ts +212 -0
  132. package/dist/types.js +7 -0
  133. package/dist/types.js.map +1 -0
  134. package/dist/util.d.ts +6 -0
  135. package/dist/util.js +28 -0
  136. package/dist/util.js.map +1 -0
  137. package/package.json +66 -0
  138. package/skills/slaide/SKILL.md +64 -0
  139. package/skills/slaide/agents/openai.yaml +12 -0
  140. package/skills/slaide/examples/branded-deck.slaide +198 -0
  141. package/skills/slaide/examples/branded-theme.slaide.yaml +187 -0
  142. package/skills/slaide/examples/sample.slaide +98 -0
  143. package/skills/slaide/reference.md +177 -0
  144. package/skills/slaide/themes.md +171 -0
  145. package/themes/aurora.slaide.yaml +136 -0
@@ -0,0 +1,171 @@
1
+ # slaide master / theme authoring
2
+
3
+ A **master** is a YAML file defining the whole visual system as data, so one theme serves many decks and an agent can both *use* and *author* it. Reference it from a deck with `master: ./your-theme.slaide.yaml` (omit → bundled `aurora`).
4
+
5
+ ## Top-level shape
6
+
7
+ ```yaml
8
+ schema: slaide/1
9
+ name: my-theme
10
+ canvas: { aspect: "16:9", width: 1920, height: 1080 } # fixed design space, scaled to fit
11
+ fonts: { … } # named roles, auto-imported from Google
12
+ typeScale: { … } # named size steps
13
+ colors: { palette, roles }
14
+ gradients: { … } # named CSS gradients (for .grad text + slot fill)
15
+ tokens: { … } # raw CSS custom-property overrides
16
+ backgrounds:{ … } # named background layers
17
+ variants: { … } # scoped token overrides
18
+ transitions:{ default, duration }
19
+ chrome: { header, footer, logo, logoPos }
20
+ ui: { progress }
21
+ layouts: { … } # grid templates with typed slots
22
+ ```
23
+
24
+ Sizes are in canvas px; the runtime scales each slide to fit (letterboxed). Match your source design (e.g. `1920×1080`) so px == design pt.
25
+
26
+ ## fonts / typeScale
27
+
28
+ **Use real Google Fonts only** (`provider: google`) — pick families that actually exist on Google Fonts and embed (avoid **JetBrains Mono**, which doesn't). A `display` (headings) + a `sans` (body) is plenty; add a `mono` role only if the deck shows code. A `provider: system`/`local` font that isn't a common system font (Arial, Calibri, Georgia…) **warns** (`non-embeddable-font`): it won't embed in the `.pptx`, so PowerPoint substitutes it off your machine.
29
+
30
+ ```yaml
31
+ fonts:
32
+ sans: { family: "Open Sans", provider: google, weights: [400, 600, 700] }
33
+ display: { family: "Open Sans", provider: google, weights: [800] }
34
+ typeScale:
35
+ base: "26px"
36
+ ratio: 1.2
37
+ steps: { stat: "104px", hero: "82px", h1: "66px", h2: "44px", h3: "34px", h4: "28px", body: "26px", caption: "21px", small: "18px" }
38
+ ```
39
+
40
+ A step is an explicit size string (`"72px"`, exact — recommended) **or** an integer exponent on the modular scale (`base × ratio^step`). Canonical names `stat hero h1 h2 h3 h4 body caption small` (add your own); `body`/`caption`/`small`/`h*` also size Markdown text & headings.
41
+
42
+ ## colors / gradients
43
+
44
+ Two tiers: `palette` = raw named colours; `roles` = semantic aliases referencing palette via `{palette.x}`. Content/layouts reference **roles** so a reskin swaps the palette. Both palette keys and role names work as inline classes (`[x]{.blue}`, `[x]{.accent}`).
45
+
46
+ ```yaml
47
+ colors:
48
+ palette: { navy: "#0B1220", white: "#F8FAFC", blue: "#5B8CFF", muted: "#8B93A7" }
49
+ roles: { background: "{palette.navy}", text: "{palette.white}", heading: "{palette.white}", accent: "{palette.blue}", muted: "{palette.muted}" }
50
+ gradients:
51
+ brand: "linear-gradient(100deg, #5B8CFF 0%, #A855F7 50%, #2DD4BF 100%)" # .grad → this one
52
+ purple: "linear-gradient(120deg, #A855F7, #EC4899)" # .grad-purple
53
+ ```
54
+
55
+ ## tokens
56
+
57
+ Raw CSS custom-property overrides (spacing, chrome metrics, chart + code styling). Exact names below — a misspelling is silently ignored.
58
+
59
+ ```yaml
60
+ tokens:
61
+ "--slide-padding": "96px" # content inset
62
+ "--chrome-pad": "96px" # header/footer/logo horizontal inset
63
+ "--chrome-top": "54px" "--chrome-bottom": "54px" # band / corner-logo offsets
64
+ "--chrome-size": "22px" "--chrome-foot-size": "24px" "--logo-h": "40px"
65
+ "--chart-colors": "#5B8CFF, #A855F7, #2DD4BF" # ordered series palette for ```echart
66
+ "--code-bg": "#0E0B07" "--code-fg": "#FBF7F0" "--code-inline-bg": "#F0E8D9"
67
+ "--code-pad": "1.1em 1.3em" "--code-radius": "12px" # note `--code-pad`, not `-padding`
68
+ ```
69
+
70
+ The `--code-*` tokens are the only way to style code panels (no per-fence highlighting); set them in `tokens:` to restyle every `pre`/`code` at once.
71
+
72
+ ## backgrounds
73
+
74
+ Named layers referenced by layouts (`background: cover`) or slides. Native types: **`solid`**, **`gradient`** (linear only — `stops` + `angle`), **`image`** (`src`, `fit`, `dim`). **There is no `radial` type** — for a dark hero glow, point an `image` background at an SVG with a `radialGradient` (a small file or `data:` URI).
75
+
76
+ ```yaml
77
+ backgrounds:
78
+ cover: { type: gradient, stops: ["#0B0B12", "#191933"], angle: 130 }
79
+ hero: { type: image, src: "./assets/hero-glow.svg", fit: cover } # dark rect + radialGradient
80
+ plain: { type: solid, color: "{palette.navy}" }
81
+ ```
82
+
83
+ > **Split (two-tone) backgrounds break the contrast lint.** `low-contrast` resolves one worst-case background per slide, so a hard-edge `split` gradient makes every text slot fail `--strict`. For a two-tone slide, give each side its own slot `box:`/`bg:` surface instead.
84
+
85
+ ## variants
86
+
87
+ Scoped token overrides invoked by one word from a slide (`variant: light`):
88
+
89
+ ```yaml
90
+ variants:
91
+ light: { roles: { background: "#EEEFF1", text: "#0B1220", heading: "#0B1220" } }
92
+ ```
93
+
94
+ ## chrome
95
+
96
+ Header/footer bands + a corner logo on every slide (toggle per layout/slide). Band cells `left`/`center`/`right` may hold `{{placeholders}}`; `logo` is inline SVG (inherits text colour via `currentColor`); `logoPos` ∈ `top-left|top-right|bottom-left|bottom-right`.
97
+
98
+ ```yaml
99
+ chrome:
100
+ header: { right: "{{date}} | © {{company}}" }
101
+ footer: { left: "{{footer}}", right: "{{pagePadded}} / {{totalPadded}}" }
102
+ logoPos: top-left
103
+ logo: '<svg viewBox="0 0 96 40" fill="none" stroke="currentColor" stroke-width="2.1"><path d="M2 35 L17 6 L32 35"/></svg>'
104
+ ```
105
+
106
+ Per layout: `chrome: header|footer|both|false` and `logo: false`; per-slide frontmatter overrides both. **A header/footer corner cell and the `logo` in the *same* corner overlap silently — put them in different corners.**
107
+
108
+ ## ui
109
+
110
+ `ui: { progress: true }` — web position bar + counter (default on; never affects PDF). Per-deck override: `progress: false` in headmatter.
111
+
112
+ ## layouts
113
+
114
+ Grid templates. `areas` = a `grid-template-areas` map (each entry = one row of space-separated slot names); `slots` = the typed regions content routes into. Optional `align: start|center|end` (vertical), `rows`, `cols`, `gap`, `padding`, `background`, `variant`. Keep `areas` rectangular; every slot in `slots` must appear in `areas`.
115
+
116
+ ```yaml
117
+ layouts:
118
+ statement:
119
+ background: navy
120
+ align: center
121
+ areas: ["title title title", "s1 s2 s3"]
122
+ rows: "auto 1fr"
123
+ cols: "1fr 1fr 1fr"
124
+ gap: "3em"
125
+ slots:
126
+ title: { type: title, style: { size: h1, weight: "800" } }
127
+ s1: { type: body, style: { align: center, valign: center } }
128
+ s2: { type: body, style: { align: center, valign: center } }
129
+ s3: { type: body, style: { align: center, valign: center } }
130
+ ```
131
+
132
+ **Slot `type`** defaults: `title`, `subtitle`, `body`, `image`, `media`, `quote`, `caption`.
133
+
134
+ > **Dark sections — read this or you ship invisible text.** A layout's `background` and its slots' text colour resolve **independently**; roles default to the *light* variant, so a dark `background` with default-role slots renders **dark-on-dark**. Fix: bind **`variant: dark`** to the layout (roles flip automatically — cleanest), or set explicit light `color:` on the slots. The compiler emits **`low-contrast`** when resolved text ≈ background (≈ under 2.5:1, including a `box:` panel's own surface), so `--strict` catches it. Bold/links inherit surrounding colour — never hand-colour a dark span onto a dark ground.
135
+
136
+ > **`align` centers the whole grid (title included).** To pin the title top and centre only the body, leave layout `align` unset and put `valign: center` on the **content** slots (in a tall `1fr` row).
137
+
138
+ ### Slot `style` keys (all optional)
139
+
140
+ | key | effect | accepts |
141
+ |---|---|---|
142
+ | `font` | font-family | font role (`display`) |
143
+ | `size` | font-size | type-scale step (`h1`) **or** explicit (`"72px"`) |
144
+ | `color` | text colour | a role/palette **name** (preferred — stays on-theme). A raw hex / CSS colour (`"#0af"`, `rgb(...)`) also works as an escape hatch but is best avoided; prefer adding the colour to the palette. An unknown **name** (not a hex/CSS colour) **warns** |
145
+ | `fill` | gradient **text** fill | gradient name (`brand`); unknown **warns** (`unknown-gradient`) |
146
+ | `align` | horizontal **text** align | `left`/`center`/`right` |
147
+ | `valign` | **vertical** align in cell | `top`/`center`/`bottom` |
148
+ | `justify` | horizontal self-align in cell — use for an **image/box** (`align` only moves text) | `left`/`center`/`right` |
149
+ | `weight` / `leading` / `transform` / `italic` | weight / line-height / `uppercase` / `true` | — |
150
+ | `maxw` | max-width (title wrap control) | e.g. `"14ch"` |
151
+ | `box` | surface panel (bg + padding + radius) | `true`, a colour role/palette name (preferred), a **named master gradient** (`box: brand` → padded, rounded, gradient hero/closing panel), or a raw hex / CSS colour / gradient |
152
+ | `bg` | background of the slot region | any CSS colour / gradient (literal) |
153
+ | `anchor` | absolutely position the slot | `"x% y% w% h%"` of the canvas |
154
+
155
+ `color:`/`box:`/`fill:` are validated against the master — an unresolved name warns, so `validate` can't call an invisible-text slide valid. Run `slaide slots` for legal names.
156
+
157
+ ## Marks / utility classes
158
+
159
+ Inline `[text]{.class}` resolves against this master: **colour** = any `palette`/`roles` name; **gradient** = `.grad` (→ `brand`) and `.grad-<name>`; **size** = only `.xs .sm .md .lg .xl .xxl .huge` (→ `small caption h3 h2 h1 hero stat`; custom steps aren't inline-addressable — use slot `size:`); **utility** `.bold` `.muted`, image `.round` `.cover` `.shadow`.
160
+
161
+ ## Bundled `aurora` layouts
162
+
163
+ `cover`, `title-content`, `section`, `two-cols`, `image-right`, `image-left`, `content-sidebar`, `quote`, `full-bleed`, `blank`. (Bundled `two-cols` ≠ a custom theme's `two-col` — names are per-theme.)
164
+
165
+ ## Tips for AI authors
166
+
167
+ - Reference **roles** and **scale steps**, not raw hex/px, so a reskin is a palette swap. Add a layout by adding an `areas` map + `slots` — no code.
168
+ - **Cards (`box:`) — kill hollow tops.** A `box:` is bg + padding + radius only (solid colour — **no gradient/shadow/border**). With no `valign` a card **stretches to its grid row** and pins text to the top — hollow in a tall `1fr` row. Two fixes: **`valign: center`** shrinks the card to its content and centres it; or, to keep a row of cards equal height, put them in an **`auto`** row and centre that block with spacer rows (`rows: "auto 1fr auto 1fr"`). On a light ground, tint the card surface off-white vs the page so it reads.
169
+ - **Signature, not just tidy:** give the deck one repeated motif (a faint logo-derived shape or a recurring accent panel) and vary composition slide to slide. Make the **cover and closing command** — don't leave half the canvas empty: pair a strong text column with a full-height brand/gradient panel or full-bleed motif.
170
+ - **Inline `svg` needs a size:** give the `<svg>` explicit `width`/`height`, or a centered zero-size SVG collapses and vanishes.
171
+ - **A deck about slaide itself?** Use the shipped brand in the sibling `slaide-hugo` repo (`themes/slaide/assets/css/slaide.css` `:root`; `static/images/slaide-*.svg`): slate-azure gradient `#243B6B→#3E6FB0→#6FA8DC` on cream `#FBF7F0` / ink `#15120E`; IBM Plex Sans + JetBrains Mono + Fraunces.
@@ -0,0 +1,136 @@
1
+ # yaml-language-server: $schema=https://getslaide.com/schema/v1.json
2
+ schema: slaide/1
3
+ name: aurora
4
+ description: A refined dark theme with a serif display face and an electric-blue accent.
5
+
6
+ canvas: { aspect: "16:9", width: 1280, height: 720 }
7
+
8
+ fonts:
9
+ sans: { family: "Inter", provider: google, weights: [400, 500, 600, 700] }
10
+ display: { family: "Fraunces", provider: google, weights: [400, 600, 900] }
11
+ mono: { family: "JetBrains Mono", provider: google, weights: [400, 600] }
12
+
13
+ typeScale:
14
+ base: "25px"
15
+ ratio: 1.265
16
+ steps: { h1: 4, h2: 3, h3: 2, h4: 1, body: 0, caption: -1 }
17
+
18
+ colors:
19
+ palette:
20
+ ink: "#0B0B12"
21
+ deep: "#191933"
22
+ paper: "#F6F7FB"
23
+ brand: "#6C8Cff"
24
+ accent: "#FF6B9A"
25
+ mint: "#54E0A8"
26
+ muted: "#9AA0B8"
27
+ surface: "#15151F"
28
+ roles:
29
+ background: "{palette.ink}"
30
+ surface: "{palette.surface}"
31
+ text: "{palette.paper}"
32
+ heading: "{palette.paper}"
33
+ accent: "{palette.brand}"
34
+ link: "{palette.accent}"
35
+ muted: "{palette.muted}"
36
+
37
+ backgrounds:
38
+ cover: { type: gradient, stops: ["#0B0B12", "#191933", "#0B0B12"], angle: 130 }
39
+ section: { type: gradient, stops: ["#6C8Cff", "#FF6B9A"], angle: 120 }
40
+ deep: { type: gradient, stops: ["#0B0B12", "#15151F"], angle: 160 }
41
+
42
+ variants:
43
+ light:
44
+ roles:
45
+ background: "{palette.paper}"
46
+ surface: "#FFFFFF"
47
+ text: "{palette.ink}"
48
+ heading: "{palette.ink}"
49
+ muted: "#5A6072"
50
+
51
+ transitions: { default: fade, duration: 420 }
52
+
53
+ layouts:
54
+ cover:
55
+ background: cover
56
+ align: center
57
+ areas: ["title", "subtitle"]
58
+ rows: "auto auto"
59
+ slots:
60
+ title: { type: title, style: { font: display, size: h1, weight: "900" } }
61
+ subtitle: { type: subtitle, style: { size: h3, color: accent } }
62
+
63
+ title-content:
64
+ areas: ["title", "body"]
65
+ rows: "auto 1fr"
66
+ slots:
67
+ title: { type: title, style: { size: h2 } }
68
+ body: { type: body }
69
+
70
+ section:
71
+ background: section
72
+ align: center
73
+ areas: ["title"]
74
+ rows: "1fr"
75
+ slots:
76
+ title: { type: title, style: { font: display, size: h1, weight: "900", align: center } }
77
+
78
+ two-cols:
79
+ areas: ["title title", "left right"]
80
+ rows: "auto 1fr"
81
+ cols: "1fr 1fr"
82
+ gap: "1.4em"
83
+ slots:
84
+ title: { type: title, style: { size: h2 } }
85
+ left: { type: body }
86
+ right: { type: body }
87
+
88
+ image-right:
89
+ areas: ["title title", "body image"]
90
+ rows: "auto 1fr"
91
+ cols: "1.1fr 1fr"
92
+ gap: "1.4em"
93
+ slots:
94
+ title: { type: title, style: { size: h2 } }
95
+ body: { type: body }
96
+ image: { type: image }
97
+
98
+ image-left:
99
+ areas: ["title title", "image body"]
100
+ rows: "auto 1fr"
101
+ cols: "1fr 1.1fr"
102
+ gap: "1.4em"
103
+ slots:
104
+ title: { type: title, style: { size: h2 } }
105
+ image: { type: image }
106
+ body: { type: body }
107
+
108
+ content-sidebar:
109
+ areas: ["title sidebar", "body sidebar"]
110
+ rows: "auto 1fr"
111
+ cols: "1.7fr 1fr"
112
+ gap: "1.4em"
113
+ slots:
114
+ title: { type: title, style: { size: h2 } }
115
+ body: { type: body }
116
+ sidebar: { type: body, style: { size: caption, color: muted } }
117
+
118
+ quote:
119
+ align: center
120
+ areas: ["quote"]
121
+ rows: "1fr"
122
+ slots:
123
+ quote: { type: quote, style: { align: center } }
124
+
125
+ full-bleed:
126
+ areas: ["media"]
127
+ rows: "1fr"
128
+ padding: "0"
129
+ slots:
130
+ media: { type: media }
131
+
132
+ blank:
133
+ areas: ["body"]
134
+ rows: "1fr"
135
+ slots:
136
+ body: { type: body }