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.
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/_includes/slides/_nested-list.njk +26 -0
- package/_includes/slides/agenda.njk +17 -0
- package/_includes/slides/center.njk +16 -0
- package/_includes/slides/code.njk +15 -0
- package/_includes/slides/columns.njk +35 -0
- package/_includes/slides/comparison.njk +29 -0
- package/_includes/slides/content.njk +22 -0
- package/_includes/slides/footer.njk +58 -0
- package/_includes/slides/funnel.njk +30 -0
- package/_includes/slides/hero.njk +27 -0
- package/_includes/slides/image-overlay.njk +21 -0
- package/_includes/slides/metrics.njk +27 -0
- package/_includes/slides/quote.njk +17 -0
- package/_includes/slides/section.njk +6 -0
- package/_includes/slides/split-wide.njk +30 -0
- package/_includes/slides/split.njk +30 -0
- package/_includes/slides/table.njk +31 -0
- package/_includes/slides/timeline.njk +30 -0
- package/_includes/slides/title.njk +17 -0
- package/_layouts/default.njk +20 -0
- package/_layouts/presentation.njk +240 -0
- package/assets/css/themes/default.css +62 -0
- package/assets/css/vendor/glightbox.min.css +1 -0
- package/assets/js/vendor/glightbox.min.js +1 -0
- package/assets/reveal.js/LICENSE +19 -0
- package/assets/reveal.js/dist/reset.css +30 -0
- package/assets/reveal.js/dist/reveal.css +8 -0
- package/assets/reveal.js/dist/reveal.esm.js +9 -0
- package/assets/reveal.js/dist/reveal.esm.js.map +1 -0
- package/assets/reveal.js/dist/reveal.js +9 -0
- package/assets/reveal.js/dist/reveal.js.map +1 -0
- package/assets/reveal.js/dist/theme/beige.css +366 -0
- package/assets/reveal.js/dist/theme/black-contrast.css +362 -0
- package/assets/reveal.js/dist/theme/black.css +359 -0
- package/assets/reveal.js/dist/theme/blood.css +392 -0
- package/assets/reveal.js/dist/theme/dracula.css +385 -0
- package/assets/reveal.js/dist/theme/fonts/league-gothic/LICENSE +2 -0
- package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.css +10 -0
- package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.eot +0 -0
- package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.ttf +0 -0
- package/assets/reveal.js/dist/theme/fonts/league-gothic/league-gothic.woff +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/LICENSE +45 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff +0 -0
- package/assets/reveal.js/dist/theme/fonts/source-sans-pro/source-sans-pro.css +39 -0
- package/assets/reveal.js/dist/theme/league.css +368 -0
- package/assets/reveal.js/dist/theme/moon.css +362 -0
- package/assets/reveal.js/dist/theme/night.css +360 -0
- package/assets/reveal.js/dist/theme/serif.css +363 -0
- package/assets/reveal.js/dist/theme/simple.css +362 -0
- package/assets/reveal.js/dist/theme/sky.css +370 -0
- package/assets/reveal.js/dist/theme/solarized.css +363 -0
- package/assets/reveal.js/dist/theme/white-contrast.css +362 -0
- package/assets/reveal.js/dist/theme/white.css +359 -0
- package/assets/reveal.js/dist/theme/white_contrast_compact_verbatim_headers.css +360 -0
- package/assets/reveal.js/plugin/highlight/highlight.esm.js +5 -0
- package/assets/reveal.js/plugin/highlight/highlight.js +5 -0
- package/assets/reveal.js/plugin/highlight/monokai.css +71 -0
- package/assets/reveal.js/plugin/highlight/plugin.js +439 -0
- package/assets/reveal.js/plugin/highlight/zenburn.css +80 -0
- package/assets/reveal.js/plugin/markdown/markdown.esm.js +7 -0
- package/assets/reveal.js/plugin/markdown/markdown.js +7 -0
- package/assets/reveal.js/plugin/markdown/plugin.js +491 -0
- package/assets/reveal.js/plugin/notes/notes.esm.js +1 -0
- package/assets/reveal.js/plugin/notes/notes.js +1 -0
- package/assets/reveal.js/plugin/notes/plugin.js +267 -0
- package/assets/reveal.js/plugin/notes/speaker-view.html +898 -0
- package/cli.js +80 -0
- package/data/site.json +3 -0
- package/index.js +153 -0
- package/lib/export-pdf.js +137 -0
- package/lib/init.js +154 -0
- package/lib/renumber.js +181 -0
- package/lib/validate.js +196 -0
- package/package.json +76 -0
- package/scaffold/CLAUDE.md +283 -0
- package/scaffold/README.md +29 -0
- package/scaffold/_template/index.md +78 -0
- package/scaffold/_template/outline.md +32 -0
- package/scaffold/claude-commands/add-slide.md +61 -0
- package/scaffold/claude-commands/create-outline.md +75 -0
- package/scaffold/claude-commands/new-presentation.md +218 -0
- package/scaffold/claude-commands/refine-slides.md +62 -0
- package/scaffold/claude-commands/research-topic.md +66 -0
- package/scaffold/claude-commands/validate.md +47 -0
- package/scaffold/claude-settings.json +7 -0
- package/scaffold/eleventy.config.js +11 -0
- package/scaffold/gitignore +5 -0
- package/scaffold/my-first-deck/index.md +26 -0
- package/scaffold/nvmrc +1 -0
- package/scaffold/package.json.tmpl +25 -0
- package/scaffold/presentations.11tydata.js +11 -0
- package/scaffold/styles.css +3 -0
- 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
|
+
[](https://www.npmjs.com/package/lazyslides)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](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>
|