lazyslides 0.1.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/_includes/slides/_nested-list.njk +26 -0
  4. package/_includes/slides/agenda.njk +17 -0
  5. package/_includes/slides/center.njk +16 -0
  6. package/_includes/slides/code.njk +15 -0
  7. package/_includes/slides/columns.njk +35 -0
  8. package/_includes/slides/comparison.njk +29 -0
  9. package/_includes/slides/content.njk +22 -0
  10. package/_includes/slides/footer.njk +58 -0
  11. package/_includes/slides/funnel.njk +30 -0
  12. package/_includes/slides/hero.njk +27 -0
  13. package/_includes/slides/image-overlay.njk +21 -0
  14. package/_includes/slides/metrics.njk +27 -0
  15. package/_includes/slides/quote.njk +17 -0
  16. package/_includes/slides/section.njk +6 -0
  17. package/_includes/slides/split-wide.njk +30 -0
  18. package/_includes/slides/split.njk +30 -0
  19. package/_includes/slides/table.njk +31 -0
  20. package/_includes/slides/timeline.njk +30 -0
  21. package/_includes/slides/title.njk +17 -0
  22. package/_layouts/default.njk +20 -0
  23. package/_layouts/presentation.njk +240 -0
  24. package/assets/css/themes/default.css +62 -0
  25. package/assets/css/vendor/glightbox.min.css +1 -0
  26. package/assets/js/vendor/glightbox.min.js +1 -0
  27. package/assets/reveal.js/LICENSE +19 -0
  28. package/assets/reveal.js/dist/reset.css +30 -0
  29. package/assets/reveal.js/dist/reveal.css +8 -0
  30. package/assets/reveal.js/dist/reveal.esm.js +9 -0
  31. package/assets/reveal.js/dist/reveal.esm.js.map +1 -0
  32. package/assets/reveal.js/dist/reveal.js +9 -0
  33. package/assets/reveal.js/dist/reveal.js.map +1 -0
  34. package/assets/reveal.js/dist/theme/beige.css +366 -0
  35. package/assets/reveal.js/dist/theme/black-contrast.css +362 -0
  36. package/assets/reveal.js/dist/theme/black.css +359 -0
  37. package/assets/reveal.js/dist/theme/blood.css +392 -0
  38. package/assets/reveal.js/dist/theme/dracula.css +385 -0
  39. package/assets/reveal.js/dist/theme/fonts/league-gothic/LICENSE +2 -0
  40. package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.css +10 -0
  41. package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.eot +0 -0
  42. package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.ttf +0 -0
  43. package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.woff +0 -0
  44. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/LICENSE +45 -0
  45. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot +0 -0
  46. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf +0 -0
  47. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff +0 -0
  48. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot +0 -0
  49. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf +0 -0
  50. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff +0 -0
  51. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot +0 -0
  52. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf +0 -0
  53. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff +0 -0
  54. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot +0 -0
  55. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf +0 -0
  56. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff +0 -0
  57. package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro.css +39 -0
  58. package/assets/reveal.js/dist/theme/league.css +368 -0
  59. package/assets/reveal.js/dist/theme/moon.css +362 -0
  60. package/assets/reveal.js/dist/theme/night.css +360 -0
  61. package/assets/reveal.js/dist/theme/serif.css +363 -0
  62. package/assets/reveal.js/dist/theme/simple.css +362 -0
  63. package/assets/reveal.js/dist/theme/sky.css +370 -0
  64. package/assets/reveal.js/dist/theme/solarized.css +363 -0
  65. package/assets/reveal.js/dist/theme/white-contrast.css +362 -0
  66. package/assets/reveal.js/dist/theme/white.css +359 -0
  67. package/assets/reveal.js/dist/theme/white_contrast_compact_verbatim_headers.css +360 -0
  68. package/assets/reveal.js/plugin/highlight/highlight.esm.js +5 -0
  69. package/assets/reveal.js/plugin/highlight/highlight.js +5 -0
  70. package/assets/reveal.js/plugin/highlight/monokai.css +71 -0
  71. package/assets/reveal.js/plugin/highlight/plugin.js +439 -0
  72. package/assets/reveal.js/plugin/highlight/zenburn.css +80 -0
  73. package/assets/reveal.js/plugin/markdown/markdown.esm.js +7 -0
  74. package/assets/reveal.js/plugin/markdown/markdown.js +7 -0
  75. package/assets/reveal.js/plugin/markdown/plugin.js +491 -0
  76. package/assets/reveal.js/plugin/notes/notes.esm.js +1 -0
  77. package/assets/reveal.js/plugin/notes/notes.js +1 -0
  78. package/assets/reveal.js/plugin/notes/plugin.js +267 -0
  79. package/assets/reveal.js/plugin/notes/speaker-view.html +898 -0
  80. package/cli.js +80 -0
  81. package/data/site.json +3 -0
  82. package/index.js +153 -0
  83. package/lib/export-pdf.js +137 -0
  84. package/lib/init.js +154 -0
  85. package/lib/renumber.js +181 -0
  86. package/lib/validate.js +196 -0
  87. package/package.json +76 -0
  88. package/scaffold/CLAUDE.md +283 -0
  89. package/scaffold/README.md +29 -0
  90. package/scaffold/_template/index.md +78 -0
  91. package/scaffold/_template/outline.md +32 -0
  92. package/scaffold/claude-commands/add-slide.md +61 -0
  93. package/scaffold/claude-commands/create-outline.md +75 -0
  94. package/scaffold/claude-commands/new-presentation.md +218 -0
  95. package/scaffold/claude-commands/refine-slides.md +62 -0
  96. package/scaffold/claude-commands/research-topic.md +66 -0
  97. package/scaffold/claude-commands/validate.md +47 -0
  98. package/scaffold/claude-settings.json +7 -0
  99. package/scaffold/eleventy.config.js +11 -0
  100. package/scaffold/gitignore +5 -0
  101. package/scaffold/my-first-deck/index.md +26 -0
  102. package/scaffold/nvmrc +1 -0
  103. package/scaffold/package.json.tmpl +25 -0
  104. package/scaffold/presentations.11tydata.js +11 -0
  105. package/scaffold/styles.css +3 -0
  106. package/src/styles.css +2077 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LazySlides Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # LazySlides
2
+
3
+ [![npm version](https://img.shields.io/npm/v/lazyslides)](https://www.npmjs.com/package/lazyslides)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)
6
+
7
+ Build things, not slides. For people who are too lazy to build Powerpoints and still have to... somehow.
8
+
9
+ Leverage your favorite AI agent to build simple, yet meaningful slide decks.
10
+
11
+ Built on [Eleventy](https://www.11ty.dev/), [Reveal.js](https://revealjs.com), and [Tailwind CSS](https://tailwindcss.com).
12
+
13
+ [Live demo](https://chrisissorry.github.io/lazyslides/)
14
+
15
+ ## Why
16
+
17
+ AI tools like ChatGPT, Gamma, and Manus generate entire slide decks — but the output is generic, you can't edit one slide without regenerating the whole thing, and exports to PowerPoint break layouts. The underlying problem hasn't changed: content and design are still tangled together.
18
+
19
+ LazySlides separates them. You write slides as structured YAML — the format AI models read and write natively. The engine handles all rendering and styling. This means:
20
+
21
+ - **Edit one slide without touching the rest** — each slide is an independent YAML block
22
+ - **You control every word** — no AI-generated filler text, no walls of bullets
23
+ - **Clean git diffs** — review slide changes line by line in pull requests
24
+ - **Validation catches errors before your audience does** — schema checks run on every build
25
+ - **Composable** — copy slides between decks, merge presentations, build libraries
26
+
27
+ ## Features
28
+
29
+ - **17 slide templates** — title, content, metrics, comparison, timeline, funnel, code, and more
30
+ - **Speaker view** — press S for notes, timer, and next-slide preview
31
+ - **Section progress bar** — visual progress dots with section markers; click to jump between sections
32
+ - **Source references** — attach citations to any slide, rendered as linked footnotes
33
+ - **Image lightbox** — click any image to view fullscreen via GLightbox
34
+ - **Syntax highlighting** — code slides with language-aware highlighting
35
+ - **PDF export** — one command via DeckTape at 1920x1080
36
+ - **Themeable** — swap colors, fonts, and branding with a single CSS file
37
+ - **Extensible** — add custom slide templates to your project as needed
38
+ - **Validation** — schema checks catch YAML errors before your audience does
39
+ - **Keyboard navigation** — arrow keys, Escape for overview, F for fullscreen
40
+
41
+ ## Workflow
42
+
43
+ Research a topic, outline the structure, generate YAML, iterate. Any AI tool can handle steps 1-3 — LazySlides ships with slash commands for Claude Code to handle it, but the outline format is plain markdown that works in Claude Desktop, ChatGPT, or any other tool.
44
+
45
+ 1. **Research** — gather data, quotes, and examples
46
+ 2. **Outline** — structure sections and slides in `outline.md`
47
+ 3. **Draft** — convert the outline into YAML slides (AI does this in seconds)
48
+ 4. **Iterate** — preview, refine content, re-validate
49
+
50
+ You focus on what your presentation content. The engine ties it to themed slides.
51
+
52
+ ## Setup
53
+
54
+ Requires Node.js 22+ and pnpm.
55
+
56
+ ```bash
57
+ pnpm dlx lazyslides init my-deck
58
+ cd my-deck
59
+ pnpm install
60
+ pnpm run dev
61
+ ```
62
+
63
+ Open [localhost:8080/presentations/my-first-deck/](http://localhost:8080/presentations/my-first-deck/) — the starter deck created by `init`. Edit `presentations/my-first-deck/index.md` to make it yours.
64
+
65
+ ## Outline format
66
+
67
+ Each project includes `presentations/_template/outline.md`:
68
+
69
+ ```markdown
70
+ # Introducing LazySlides
71
+
72
+ ## Metadata
73
+ - **Audience:** Developers, technical founders, presentation-heavy professionals
74
+ - **Purpose:** Show how YAML + AI replaces manual slide design
75
+ - **Speaker:** Chrisissorry, creator of LazySlides
76
+ - **Duration:** 20 minutes
77
+
78
+ ## Key Messages
79
+ 1. Presentations are broken — manual layout and proprietary formats waste hours
80
+ 2. YAML + templates = separation of concerns
81
+ 3. AI writes structured YAML natively, enabling fast generation
82
+
83
+ ## Outline
84
+
85
+ ### Section 1: The Problem
86
+ Purpose: Establish that current tools (including AI ones) don't fix the core issue
87
+
88
+ #### Slide 1.1: Death by PowerPoint
89
+ - **Key point:** AI tools moved the pain, didn't remove it
90
+ - **Template:** content
91
+ - **Notes:** Generic output, all-or-nothing generation, broken exports
92
+
93
+ #### Slide 1.2: What if slides were just data?
94
+ - **Key point:** Pure YAML in, professional slides out
95
+ - **Template:** center
96
+ ```
97
+
98
+ The scaffold includes a blank version at `presentations/_template/outline.md`. Feed it to any AI tool along with your source material (meeting notes, research, a brief) and it produces a structured outline ready for YAML generation.
99
+
100
+ ## Writing slides
101
+
102
+ Each presentation is a single `index.md` with YAML frontmatter. No HTML, no Markdown body — just structured data:
103
+
104
+ ```yaml
105
+ ---
106
+ title: My Presentation
107
+ slides:
108
+ - template: title
109
+ title: Hello World
110
+ subtitle: My first deck
111
+
112
+ - template: content
113
+ title: Key Points
114
+ items:
115
+ - First point
116
+ - Second point
117
+
118
+ - template: metrics
119
+ title: Results
120
+ metrics:
121
+ - value: "42%"
122
+ label: Growth
123
+ color: mint
124
+ ---
125
+ ```
126
+
127
+ Supporting files (outlines, notes, PDFs, images) go alongside `index.md` in the same folder — only `index.md` is processed as a presentation.
128
+
129
+ ## Templates
130
+
131
+ 17 built-in templates cover common slide patterns:
132
+
133
+ | Template | Description |
134
+ |----------|-------------|
135
+ | `title` | Opening slide with logo, title, subtitle, author |
136
+ | `section` | Chapter divider |
137
+ | `content` | Bullet points, supports nested lists |
138
+ | `center` | Centered statement or image |
139
+ | `hero` | Full-bleed background image with text overlay |
140
+ | `metrics` | Key statistics with colored cards |
141
+ | `comparison` | Two-column comparison table |
142
+ | `columns` | Side-by-side content |
143
+ | `split` | 1/3 image + 2/3 content |
144
+ | `split-wide` | 1/3 content + 2/3 image |
145
+ | `quote` | Quotation with attribution |
146
+ | `timeline` | Horizontal timeline |
147
+ | `funnel` | Progressive narrowing stages |
148
+ | `table` | Data table |
149
+ | `code` | Code snippet with syntax highlighting |
150
+ | `image-overlay` | Full image with positioned text box |
151
+ | `agenda` | Clickable table of contents |
152
+
153
+ See `CLAUDE.md` for the full field reference for each template.
154
+
155
+ ## Slash commands (Claude Code)
156
+
157
+ The scaffolded project includes Claude Code slash commands for the full workflow:
158
+
159
+ | Command | What it does |
160
+ |---------|-------------|
161
+ | `/new-presentation` | Create a presentation from scratch |
162
+ | `/add-slide` | Add a slide to an existing deck |
163
+ | `/research-topic` | Research a topic for data-driven slides |
164
+ | `/create-outline` | Plan structure before writing YAML |
165
+ | `/refine-slides` | Improve an existing presentation |
166
+ | `/validate` | Check YAML and fix issues |
167
+
168
+ The project ships a `CLAUDE.md` with the complete template schema, so Claude Code knows every field of every template.
169
+
170
+ ## Commands
171
+
172
+ ```bash
173
+ pnpm run dev # Dev server with hot reload
174
+ pnpm run build # Production build to _site/
175
+ pnpm run validate # Validate presentation YAML
176
+ pnpm run renumber # Renumber slide comments
177
+ pnpm run pdf # Export to PDF via DeckTape
178
+ ```
179
+
180
+ ## Theming
181
+
182
+ Drop a CSS file into `themes/` to override design tokens:
183
+
184
+ ```css
185
+ :root {
186
+ --color-primary-500: #your-brand-color;
187
+ --footer-brand-name: 'Your Brand';
188
+ }
189
+ ```
190
+
191
+ Set `theme: your-theme-name` in presentation frontmatter.
192
+
193
+ ## Updating
194
+
195
+ ```bash
196
+ pnpm update lazyslides
197
+ ```
198
+
199
+ Content stays untouched — only the engine updates.
200
+
201
+ ## Contributing
202
+
203
+ See [CONTRIBUTING.md](CONTRIBUTING.md). AI-assisted contributions welcome — see [AGENTS.md](AGENTS.md).
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,26 @@
1
+ {# Reusable macro for rendering nested lists (map syntax + legacy object syntax) #}
2
+ {% macro renderItems(items, ordered) %}
3
+ <{{ "ol" if ordered else "ul" }}>
4
+ {% for item in items %}
5
+ <li>
6
+ {% if item is mapping %}
7
+ {% for key, children in item %}
8
+ {{ key }}
9
+ <ul>
10
+ {% for sub in children %}<li>{{ sub }}</li>{% endfor %}
11
+ </ul>
12
+ {% endfor %}
13
+ {% elif item.text %}
14
+ {{ item.text }}
15
+ {% if item.sub_items %}
16
+ <ul>
17
+ {% for sub in item.sub_items %}<li>{{ sub }}</li>{% endfor %}
18
+ </ul>
19
+ {% endif %}
20
+ {% else %}
21
+ {{ item }}
22
+ {% endif %}
23
+ </li>
24
+ {% endfor %}
25
+ </{{ "ol" if ordered else "ul" }}>
26
+ {% endmacro %}
@@ -0,0 +1,17 @@
1
+ {# TEMPLATE: agenda — Clickable table of contents for presentation sections. #}
2
+ <section class="slide-agenda">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title | default("Agenda") }}</h2>
6
+ </div>
7
+ <ol class="agenda-list">
8
+ {% for item in slide.sections %}
9
+ <li><a href="#/{{ item.slide }}">{{ item.title }}</a></li>
10
+ {% endfor %}
11
+ </ol>
12
+ </div>
13
+ {% set reference = slide.reference %}
14
+ {% set reference_link = slide.reference_link %}
15
+ {% set references = slide.references %}
16
+ {% include "slides/footer.njk" %}
17
+ </section>
@@ -0,0 +1,16 @@
1
+ {# TEMPLATE: center — Centered statement, image, or both. Image has lightbox on click. #}
2
+ <section class="slide-center">
3
+ <div class="slide-body">
4
+ {% if slide.title %}<h2>{{ slide.title }}</h2>{% endif %}
5
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
6
+ {% if slide.image %}
7
+ {% set img_src = slide.image | resolveImage %}
8
+ <a href="{{ img_src }}" class="glightbox"><img src="{{ img_src }}" alt="{{ slide.image_alt | default('') }}"></a>
9
+ {% endif %}
10
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
11
+ </div>
12
+ {% set reference = slide.reference %}
13
+ {% set reference_link = slide.reference_link %}
14
+ {% set references = slide.references %}
15
+ {% include "slides/footer.njk" %}
16
+ </section>
@@ -0,0 +1,15 @@
1
+ {# TEMPLATE: code — Code snippet display with syntax highlighting. #}
2
+ <section class="slide-code">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title }}</h2>
6
+ </div>
7
+ <pre><code class="language-{{ slide.language | default('javascript') }}" data-trim data-noescape>{{ slide.code }}</code></pre>
8
+ {% if slide.caption %}<p class="caption">{{ slide.caption }}</p>{% endif %}
9
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
10
+ </div>
11
+ {% set reference = slide.reference %}
12
+ {% set reference_link = slide.reference_link %}
13
+ {% set references = slide.references %}
14
+ {% include "slides/footer.njk" %}
15
+ </section>
@@ -0,0 +1,35 @@
1
+ {# TEMPLATE: columns — Two-column layout for side-by-side content. #}
2
+ <section class="slide-columns">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title }}</h2>
6
+ </div>
7
+ <div class="columns">
8
+ <div class="column">
9
+ {% if slide.left_title %}<h3>{{ slide.left_title }}</h3>{% endif %}
10
+ {% if slide.left_text %}<p>{{ slide.left_text }}</p>{% endif %}
11
+ {% if slide.left_items %}
12
+ <ul>
13
+ {% for item in slide.left_items %}<li>{{ item }}</li>
14
+ {% endfor %}
15
+ </ul>
16
+ {% endif %}
17
+ </div>
18
+ <div class="column">
19
+ {% if slide.right_title %}<h3>{{ slide.right_title }}</h3>{% endif %}
20
+ {% if slide.right_text %}<p>{{ slide.right_text }}</p>{% endif %}
21
+ {% if slide.right_items %}
22
+ <ul>
23
+ {% for item in slide.right_items %}<li>{{ item }}</li>
24
+ {% endfor %}
25
+ </ul>
26
+ {% endif %}
27
+ </div>
28
+ </div>
29
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
30
+ </div>
31
+ {% set reference = slide.reference %}
32
+ {% set reference_link = slide.reference_link %}
33
+ {% set references = slide.references %}
34
+ {% include "slides/footer.njk" %}
35
+ </section>
@@ -0,0 +1,29 @@
1
+ {# TEMPLATE: comparison — Before/after two-column table with optional highlight styling. #}
2
+ <section class="slide-comparison{% if slide.highlight == 'left' %} highlight-left{% elif slide.highlight == 'none' %} highlight-none{% endif %}">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title }}</h2>
6
+ </div>
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th>{{ slide.left_header | default("Before") }}</th>
11
+ <th>{{ slide.right_header | default("After") }}</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ {% for row in slide.rows %}
16
+ <tr>
17
+ <td>{% if row.left_icon == "bad" %}<span class="icon-bad">✗</span>{% endif %}{{ row.left }}</td>
18
+ <td>{% if row.right_icon == "good" %}<span class="icon-good">✓</span>{% endif %}{{ row.right }}</td>
19
+ </tr>
20
+ {% endfor %}
21
+ </tbody>
22
+ </table>
23
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
24
+ </div>
25
+ {% set reference = slide.reference %}
26
+ {% set reference_link = slide.reference_link %}
27
+ {% set references = slide.references %}
28
+ {% include "slides/footer.njk" %}
29
+ </section>
@@ -0,0 +1,22 @@
1
+ {# TEMPLATE: content — Standard slide with headline and bullet points. #}
2
+ {% from "slides/_nested-list.njk" import renderItems %}
3
+ <section class="slide-content">
4
+ <div class="slide-body">
5
+ <div class="slide-header">
6
+ <h2>{{ slide.title }}</h2>
7
+ {% if slide.lead %}<p class="lead">{{ slide.lead }}</p>{% endif %}
8
+ </div>
9
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
10
+ {% if slide.items %}
11
+ {{ renderItems(slide.items) }}
12
+ {% endif %}
13
+ {% if slide.ordered_items %}
14
+ {{ renderItems(slide.ordered_items, true) }}
15
+ {% endif %}
16
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
17
+ </div>
18
+ {% set reference = slide.reference %}
19
+ {% set reference_link = slide.reference_link %}
20
+ {% set references = slide.references %}
21
+ {% include "slides/footer.njk" %}
22
+ </section>
@@ -0,0 +1,58 @@
1
+ <div class="slide-footer">
2
+ <div class="footer-left">
3
+ <span class="current">{{ slide_index }}</span>
4
+ <span class="separator">/</span>
5
+ <span class="total">{{ total_slides }}</span>
6
+ </div>
7
+
8
+ <div class="footer-center">
9
+ <div class="logo-small">
10
+ <!-- Default logo SVG, hidden when theme provides --footer-logo-url -->
11
+ <svg class="default-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
12
+ <path d="M12 2L2 7l10 5 10-5-10-5z"/>
13
+ <path d="M2 17l10 5 10-5"/>
14
+ <path d="M2 12l10 5 10-5"/>
15
+ </svg>
16
+ </div>
17
+ <span class="footer-brand-name"></span>
18
+ </div>
19
+
20
+ <div class="footer-right">
21
+ {% if references or reference %}
22
+ <div class="sources-flyout">
23
+ <!-- Flyout panel (hidden by default) -->
24
+ <div class="sources-panel">
25
+ <div class="sources-header">Sources</div>
26
+ <ul class="sources-list">
27
+ {% if references %}
28
+ {% for ref in references %}
29
+ <li>
30
+ <span class="source-number">{{ loop.index }}.</span>
31
+ {% if ref.link %}
32
+ <a href="{{ ref.link }}" target="_blank" rel="noopener">{{ ref.name }}</a>
33
+ {% else %}
34
+ <span>{{ ref.name }}</span>
35
+ {% endif %}
36
+ </li>
37
+ {% endfor %}
38
+ {% elif reference %}
39
+ <li>
40
+ <span class="source-number">1.</span>
41
+ {% if reference_link %}
42
+ <a href="{{ reference_link }}" target="_blank" rel="noopener">{{ reference }}</a>
43
+ {% else %}
44
+ <span>{{ reference }}</span>
45
+ {% endif %}
46
+ </li>
47
+ {% endif %}
48
+ </ul>
49
+ </div>
50
+ <!-- Trigger button -->
51
+ <button class="sources-trigger" onclick="this.parentElement.classList.toggle('open')">
52
+ <span class="source-count">{% if references %}{{ references | length }}{% else %}1{% endif %}</span>
53
+ Sources
54
+ </button>
55
+ </div>
56
+ {% endif %}
57
+ </div>
58
+ </div>
@@ -0,0 +1,30 @@
1
+ {# TEMPLATE: funnel — Progressive narrowing funnel with stage labels and annotations. #}
2
+ <section class="slide-funnel">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title }}</h2>
6
+ </div>
7
+ <div class="funnel-wrapper">
8
+ <div class="funnel-grid">
9
+ {% for stage in slide.stages %}
10
+ <div class="funnel-row">
11
+ <div class="funnel-stage-wrapper">
12
+ <div class="funnel-stage stage-{{ loop.index }}">
13
+ <span class="stage-label">{{ stage.label }}</span>
14
+ </div>
15
+ </div>
16
+ <div class="funnel-annotation">
17
+ <span class="annotation-arrow">→</span>
18
+ <span class="annotation-text">{{ stage.text }}</span>
19
+ </div>
20
+ </div>
21
+ {% endfor %}
22
+ </div>
23
+ </div>
24
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
25
+ </div>
26
+ {% set reference = slide.reference %}
27
+ {% set reference_link = slide.reference_link %}
28
+ {% set references = slide.references %}
29
+ {% include "slides/footer.njk" %}
30
+ </section>
@@ -0,0 +1,27 @@
1
+ {# TEMPLATE: hero — Full-bleed background image with dark overlay and optional text box. #}
2
+ {% if slide.image %}{% set img_src = slide.image | resolveImage %}{% endif %}
3
+ <section class="slide-hero"{% if slide.image %} style="background-image: url('{{ img_src }}'); background-size: cover; background-position: center;"{% elif slide.color %} style="background-color: {{ slide.color }};"{% endif %}>
4
+ <div class="slide-body">
5
+ <h1>{{ slide.title }}</h1>
6
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
7
+ {% if slide.box_title or slide.box_text or slide.box_items %}
8
+ <div class="text-box">
9
+ {% if slide.box_title %}<h3>{{ slide.box_title }}</h3>{% endif %}
10
+ {% if slide.box_text %}<p>{{ slide.box_text }}</p>{% endif %}
11
+ {% if slide.box_items %}
12
+ <ul>
13
+ {% for item in slide.box_items %}<li>{{ item }}</li>
14
+ {% endfor %}
15
+ </ul>
16
+ {% endif %}
17
+ </div>
18
+ {% endif %}
19
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
20
+ </div>
21
+ {% if not slide.hide_footer %}
22
+ {% set reference = slide.reference %}
23
+ {% set reference_link = slide.reference_link %}
24
+ {% set references = slide.references %}
25
+ {% include "slides/footer.njk" %}
26
+ {% endif %}
27
+ </section>
@@ -0,0 +1,21 @@
1
+ {# TEMPLATE: image-overlay — Full background image with positioned text box overlay. #}
2
+ {% set img_src = slide.image | resolveImage %}
3
+ <section class="slide-image-overlay" data-background-image="{{ img_src }}" data-background-size="cover">
4
+ <div class="slide-body">
5
+ <div class="overlay-box {{ slide.position | default('bottom-left') }}">
6
+ {% if slide.title %}<h3>{{ slide.title }}</h3>{% endif %}
7
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
8
+ {% if slide.items %}
9
+ <ul>
10
+ {% for item in slide.items %}<li>{{ item }}</li>
11
+ {% endfor %}
12
+ </ul>
13
+ {% endif %}
14
+ </div>
15
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
16
+ </div>
17
+ {% set reference = slide.reference %}
18
+ {% set reference_link = slide.reference_link %}
19
+ {% set references = slide.references %}
20
+ {% include "slides/footer.njk" %}
21
+ </section>
@@ -0,0 +1,27 @@
1
+ {# TEMPLATE: metrics — Key statistics display with 2-4 metric cards. #}
2
+ <section class="slide-metrics">
3
+ <div class="slide-body">
4
+ <div class="slide-header">
5
+ <h2>{{ slide.title }}</h2>
6
+ {% if slide.subtitle %}<p class="subtitle">{{ slide.subtitle }}</p>{% endif %}
7
+ </div>
8
+ {% set metric_count = slide.metrics | length %}
9
+ {% if metric_count == 2 %}{% set cols_class = "cols-2" %}
10
+ {% elif metric_count == 4 %}{% set cols_class = "cols-4" %}
11
+ {% else %}{% set cols_class = "cols-3" %}{% endif %}
12
+ <div class="metrics-grid {{ cols_class }}">
13
+ {% for metric in slide.metrics %}
14
+ <div class="metric-card">
15
+ <div class="value{% if metric.color %} {{ metric.color }}{% endif %}">{{ metric.value }}</div>
16
+ <div class="label">{{ metric.label }}</div>
17
+ {% if metric.context %}<div class="context">{{ metric.context }}</div>{% endif %}
18
+ </div>
19
+ {% endfor %}
20
+ </div>
21
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
22
+ </div>
23
+ {% set reference = slide.reference %}
24
+ {% set reference_link = slide.reference_link %}
25
+ {% set references = slide.references %}
26
+ {% include "slides/footer.njk" %}
27
+ </section>
@@ -0,0 +1,17 @@
1
+ {# TEMPLATE: quote — Testimonial or statement with attribution. #}
2
+ <section class="slide-quote">
3
+ <div class="slide-body">
4
+ <div class="quote-mark">"</div>
5
+ <blockquote>{{ slide.quote }}</blockquote>
6
+ {% if slide.author %}
7
+ <div class="attribution">
8
+ {% if slide.author_title %}<strong>{{ slide.author }}</strong>{{ slide.author_title }}{% else %}— {{ slide.author }}{% endif %}
9
+ </div>
10
+ {% endif %}
11
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
12
+ </div>
13
+ {% set reference = slide.reference %}
14
+ {% set reference_link = slide.reference_link %}
15
+ {% set references = slide.references %}
16
+ {% include "slides/footer.njk" %}
17
+ </section>
@@ -0,0 +1,6 @@
1
+ {# TEMPLATE: section — Chapter divider slide for separating presentation sections. #}
2
+ <section class="slide-section">
3
+ <h2>{{ slide.title }}</h2>
4
+ {% if slide.subtitle %}<p class="subtitle">{{ slide.subtitle }}</p>{% endif %}
5
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
6
+ </section>
@@ -0,0 +1,30 @@
1
+ {# TEMPLATE: split-wide — 1/3 content + 2/3 image layout (inverse of split). #}
2
+ {% from "slides/_nested-list.njk" import renderItems %}
3
+ <section class="slide-split-wide">
4
+ <div class="slide-body">
5
+ <div class="split-wide-container">
6
+ <div class="split-wide-content">
7
+ <div class="slide-header">
8
+ <h2>{{ slide.title }}</h2>
9
+ </div>
10
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
11
+ {% if slide.items %}
12
+ {{ renderItems(slide.items) }}
13
+ {% endif %}
14
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
15
+ </div>
16
+ <div class="split-wide-image">
17
+ {% if slide.image %}
18
+ {% set img_src = slide.image | resolveImage %}
19
+ <a href="{{ img_src }}" class="glightbox">
20
+ <img src="{{ img_src }}" alt="{{ slide.image_alt | default('') }}">
21
+ </a>
22
+ {% endif %}
23
+ </div>
24
+ </div>
25
+ </div>
26
+ {% set reference = slide.reference %}
27
+ {% set reference_link = slide.reference_link %}
28
+ {% set references = slide.references %}
29
+ {% include "slides/footer.njk" %}
30
+ </section>
@@ -0,0 +1,30 @@
1
+ {# TEMPLATE: split — 1/3 image + 2/3 content layout. Image has lightbox on click. #}
2
+ {% from "slides/_nested-list.njk" import renderItems %}
3
+ <section class="slide-split">
4
+ <div class="slide-body">
5
+ <div class="split-container">
6
+ <div class="split-image">
7
+ {% if slide.image %}
8
+ {% set img_src = slide.image | resolveImage %}
9
+ <a href="{{ img_src }}" class="glightbox">
10
+ <img src="{{ img_src }}" alt="{{ slide.image_alt | default('') }}">
11
+ </a>
12
+ {% endif %}
13
+ </div>
14
+ <div class="split-content">
15
+ <div class="slide-header">
16
+ <h2>{{ slide.title }}</h2>
17
+ </div>
18
+ {% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
19
+ {% if slide.items %}
20
+ {{ renderItems(slide.items) }}
21
+ {% endif %}
22
+ {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
23
+ </div>
24
+ </div>
25
+ </div>
26
+ {% set reference = slide.reference %}
27
+ {% set reference_link = slide.reference_link %}
28
+ {% set references = slide.references %}
29
+ {% include "slides/footer.njk" %}
30
+ </section>