lazyslides 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -10,7 +10,7 @@ Leverage your favorite AI agent to build simple, yet meaningful slide decks.
10
10
 
11
11
  Built on [Eleventy](https://www.11ty.dev/), [Reveal.js](https://revealjs.com), and [Tailwind CSS](https://tailwindcss.com).
12
12
 
13
- [Live demo](https://chrisissorry.github.io/lazyslides/)
13
+ [Live demo](https://chrisissorry.github.io/lazyslides/presentations/example/)
14
14
 
15
15
  ## Why
16
16
 
@@ -148,10 +148,31 @@ Supporting files (outlines, notes, PDFs, images) go alongside `index.md` in the
148
148
  | `table` | Data table |
149
149
  | `code` | Code snippet with syntax highlighting |
150
150
  | `image-overlay` | Full image with positioned text box |
151
- | `agenda` | Clickable table of contents |
151
+ | `agenda` | Clickable table of contents (supports `auto_generate: true`) |
152
152
 
153
153
  See `CLAUDE.md` for the full field reference for each template.
154
154
 
155
+ ## Footer
156
+
157
+ Every slide (except `title`) includes a footer bar with slide numbers, a logo, a brand name, and optional source references.
158
+
159
+ **Visibility** — the footer is shown by default on most templates. Hero and section slides hide it by default.
160
+
161
+ | Level | Field | Effect |
162
+ |-------|-------|--------|
163
+ | Presentation | `show_footer: false` | Hides the footer on all slides |
164
+ | Per slide | `hide_footer: true` | Hides footer on that slide |
165
+ | Per slide (hero/section) | `show_footer: true` | Shows footer on that slide |
166
+
167
+ **Branding** — customize the logo and name via CSS custom properties in a theme file:
168
+
169
+ ```css
170
+ :root {
171
+ --footer-brand-name: 'Your Brand';
172
+ --footer-logo-url: url('/path/to/logo.svg');
173
+ }
174
+ ```
175
+
155
176
  ## Slash commands (Claude Code)
156
177
 
157
178
  The scaffolded project includes Claude Code slash commands for the full workflow:
@@ -164,6 +185,8 @@ The scaffolded project includes Claude Code slash commands for the full workflow
164
185
  | `/create-outline` | Plan structure before writing YAML |
165
186
  | `/refine-slides` | Improve an existing presentation |
166
187
  | `/validate` | Check YAML and fix issues |
188
+ | `/add-template` | Create a custom slide template |
189
+ | `/add-theme` | Create a custom CSS theme |
167
190
 
168
191
  The project ships a `CLAUDE.md` with the complete template schema, so Claude Code knows every field of every template.
169
192
 
@@ -184,7 +207,6 @@ Drop a CSS file into `themes/` to override design tokens:
184
207
  ```css
185
208
  :root {
186
209
  --color-primary-500: #your-brand-color;
187
- --footer-brand-name: 'Your Brand';
188
210
  }
189
211
  ```
190
212
 
@@ -1,17 +1,29 @@
1
- {# TEMPLATE: agenda — Clickable table of contents for presentation sections. #}
1
+ {# TEMPLATE: agenda — Clickable table of contents for presentation sections.
2
+ Set auto_generate: true to build the agenda automatically from section slides.
3
+ Manual mode: provide sections array with title and slide (0-based index). #}
2
4
  <section class="slide-agenda">
3
5
  <div class="slide-body">
4
6
  <div class="slide-header">
5
7
  <h2>{{ slide.title | default("Agenda") }}</h2>
6
8
  </div>
7
9
  <ol class="agenda-list">
8
- {% for item in slide.sections %}
10
+ {% if slide.auto_generate %}
11
+ {% for s in slides %}
12
+ {% if s.template == "section" %}
13
+ <li><a href="#/{{ loop.index0 }}">{{ s.title }}</a></li>
14
+ {% endif %}
15
+ {% endfor %}
16
+ {% else %}
17
+ {% for item in slide.sections %}
9
18
  <li><a href="#/{{ item.slide }}">{{ item.title }}</a></li>
10
- {% endfor %}
19
+ {% endfor %}
20
+ {% endif %}
11
21
  </ol>
12
22
  </div>
23
+ {% if global_footer and not slide.hide_footer %}
13
24
  {% set reference = slide.reference %}
14
25
  {% set reference_link = slide.reference_link %}
15
26
  {% set references = slide.references %}
16
27
  {% include "slides/footer.njk" %}
28
+ {% endif %}
17
29
  </section>
@@ -9,8 +9,10 @@
9
9
  {% endif %}
10
10
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
11
11
  </div>
12
+ {% if global_footer and not slide.hide_footer %}
12
13
  {% set reference = slide.reference %}
13
14
  {% set reference_link = slide.reference_link %}
14
15
  {% set references = slide.references %}
15
16
  {% include "slides/footer.njk" %}
17
+ {% endif %}
16
18
  </section>
@@ -8,8 +8,10 @@
8
8
  {% if slide.caption %}<p class="caption">{{ slide.caption }}</p>{% endif %}
9
9
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
10
10
  </div>
11
+ {% if global_footer and not slide.hide_footer %}
11
12
  {% set reference = slide.reference %}
12
13
  {% set reference_link = slide.reference_link %}
13
14
  {% set references = slide.references %}
14
15
  {% include "slides/footer.njk" %}
16
+ {% endif %}
15
17
  </section>
@@ -28,8 +28,10 @@
28
28
  </div>
29
29
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
30
30
  </div>
31
+ {% if global_footer and not slide.hide_footer %}
31
32
  {% set reference = slide.reference %}
32
33
  {% set reference_link = slide.reference_link %}
33
34
  {% set references = slide.references %}
34
35
  {% include "slides/footer.njk" %}
36
+ {% endif %}
35
37
  </section>
@@ -22,8 +22,10 @@
22
22
  </table>
23
23
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
24
24
  </div>
25
+ {% if global_footer and not slide.hide_footer %}
25
26
  {% set reference = slide.reference %}
26
27
  {% set reference_link = slide.reference_link %}
27
28
  {% set references = slide.references %}
28
29
  {% include "slides/footer.njk" %}
30
+ {% endif %}
29
31
  </section>
@@ -15,8 +15,10 @@
15
15
  {% endif %}
16
16
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
17
17
  </div>
18
+ {% if global_footer and not slide.hide_footer %}
18
19
  {% set reference = slide.reference %}
19
20
  {% set reference_link = slide.reference_link %}
20
21
  {% set references = slide.references %}
21
22
  {% include "slides/footer.njk" %}
23
+ {% endif %}
22
24
  </section>
@@ -23,8 +23,10 @@
23
23
  </div>
24
24
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
25
25
  </div>
26
+ {% if global_footer and not slide.hide_footer %}
26
27
  {% set reference = slide.reference %}
27
28
  {% set reference_link = slide.reference_link %}
28
29
  {% set references = slide.references %}
29
30
  {% include "slides/footer.njk" %}
31
+ {% endif %}
30
32
  </section>
@@ -18,7 +18,7 @@
18
18
  {% endif %}
19
19
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
20
20
  </div>
21
- {% if not slide.hide_footer %}
21
+ {% if global_footer and slide.show_footer %}
22
22
  {% set reference = slide.reference %}
23
23
  {% set reference_link = slide.reference_link %}
24
24
  {% set references = slide.references %}
@@ -17,8 +17,10 @@
17
17
  </div>
18
18
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
19
19
  </div>
20
+ {% if global_footer and not slide.hide_footer %}
20
21
  {% set reference = slide.reference %}
21
22
  {% set reference_link = slide.reference_link %}
22
23
  {% set references = slide.references %}
23
24
  {% include "slides/footer.njk" %}
25
+ {% endif %}
24
26
  </section>
@@ -20,8 +20,10 @@
20
20
  </div>
21
21
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
22
22
  </div>
23
+ {% if global_footer and not slide.hide_footer %}
23
24
  {% set reference = slide.reference %}
24
25
  {% set reference_link = slide.reference_link %}
25
26
  {% set references = slide.references %}
26
27
  {% include "slides/footer.njk" %}
28
+ {% endif %}
27
29
  </section>
@@ -10,8 +10,10 @@
10
10
  {% endif %}
11
11
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
12
12
  </div>
13
+ {% if global_footer and not slide.hide_footer %}
13
14
  {% set reference = slide.reference %}
14
15
  {% set reference_link = slide.reference_link %}
15
16
  {% set references = slide.references %}
16
17
  {% include "slides/footer.njk" %}
18
+ {% endif %}
17
19
  </section>
@@ -3,4 +3,10 @@
3
3
  <h2>{{ slide.title }}</h2>
4
4
  {% if slide.subtitle %}<p class="subtitle">{{ slide.subtitle }}</p>{% endif %}
5
5
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
6
+ {% if global_footer and slide.show_footer %}
7
+ {% set reference = slide.reference %}
8
+ {% set reference_link = slide.reference_link %}
9
+ {% set references = slide.references %}
10
+ {% include "slides/footer.njk" %}
11
+ {% endif %}
6
12
  </section>
@@ -23,8 +23,10 @@
23
23
  </div>
24
24
  </div>
25
25
  </div>
26
+ {% if global_footer and not slide.hide_footer %}
26
27
  {% set reference = slide.reference %}
27
28
  {% set reference_link = slide.reference_link %}
28
29
  {% set references = slide.references %}
29
30
  {% include "slides/footer.njk" %}
31
+ {% endif %}
30
32
  </section>
@@ -23,8 +23,10 @@
23
23
  </div>
24
24
  </div>
25
25
  </div>
26
+ {% if global_footer and not slide.hide_footer %}
26
27
  {% set reference = slide.reference %}
27
28
  {% set reference_link = slide.reference_link %}
28
29
  {% set references = slide.references %}
29
30
  {% include "slides/footer.njk" %}
31
+ {% endif %}
30
32
  </section>
@@ -24,8 +24,10 @@
24
24
  </table>
25
25
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
26
26
  </div>
27
+ {% if global_footer and not slide.hide_footer %}
27
28
  {% set reference = slide.reference %}
28
29
  {% set reference_link = slide.reference_link %}
29
30
  {% set references = slide.references %}
30
31
  {% include "slides/footer.njk" %}
32
+ {% endif %}
31
33
  </section>
@@ -23,8 +23,10 @@
23
23
  </div>
24
24
  {% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
25
25
  </div>
26
+ {% if global_footer and not slide.hide_footer %}
26
27
  {% set reference = slide.reference %}
27
28
  {% set reference_link = slide.reference_link %}
28
29
  {% set references = slide.references %}
29
30
  {% include "slides/footer.njk" %}
31
+ {% endif %}
30
32
  </section>
@@ -43,6 +43,7 @@
43
43
  {% set slide_index = loop.index %}
44
44
  {% set total_slides = slides | length %}
45
45
  {% set presentation_title = title %}
46
+ {% set global_footer = show_footer if show_footer is defined else true %}
46
47
  {% include "slides/" + slide.template + ".njk" %}
47
48
  {% endfor %}
48
49
  </div>
package/index.js CHANGED
@@ -134,7 +134,7 @@ eleventyExcludeFromCollections: true
134
134
  ---
135
135
  <!DOCTYPE html>
136
136
  <html>
137
- <head><meta http-equiv="refresh" content="0; url={{ '/presentations/example/' | url }}"></head>
137
+ <head><meta http-equiv="refresh" content="0; url={{ '/presentations/' | url }}"></head>
138
138
  <body></body>
139
139
  </html>
140
140
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyslides",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Slide decks with native agentic AI integration",
5
5
  "license": "MIT",
6
6
  "author": "Chris Tietz",
@@ -42,11 +42,12 @@ theme: default
42
42
  transition: slide
43
43
  progress: true
44
44
  slideNumber: true
45
- show_footer: true
45
+ show_footer: true # show footer on slides (default: true, set false to hide globally)
46
46
  slides: # required — array of slide objects
47
47
  - template: content # required on every slide
48
48
  title: Slide Title
49
49
  # ... template-specific fields
50
+ hide_footer: true # optional — hide footer on this slide (default: false)
50
51
  notes: | # optional speaker notes (all templates)
51
52
  What to say during this slide
52
53
  ---
@@ -67,6 +68,7 @@ Fields marked with `*` are required.
67
68
  #### `section` — Chapter divider
68
69
  - `title`* — section name
69
70
  - `subtitle` — section description
71
+ - `show_footer` — show footer on this slide (default: hidden)
70
72
 
71
73
  #### `content` — Standard bullets
72
74
  - `title`* — slide title
@@ -92,7 +94,7 @@ Fields marked with `*` are required.
92
94
  - `box_title` — overlay box heading
93
95
  - `box_text` — overlay box content
94
96
  - `box_items` — overlay box bullet list
95
- - `hide_footer` — hide footer on this slide
97
+ - `show_footer` — show footer on this slide (default: hidden)
96
98
 
97
99
  #### `metrics` — Key statistics
98
100
  - `title`* — slide title
@@ -182,9 +184,10 @@ Fields marked with `*` are required.
182
184
 
183
185
  #### `agenda` — Clickable table of contents
184
186
  - `title` — slide title (default: "Agenda")
185
- - `sections`*array of section objects:
187
+ - `auto_generate`build agenda automatically from `section` slides (default: false)
188
+ - `sections` — manual array of section objects (ignored when `auto_generate` is true):
186
189
  - `title`* — section name
187
- - `slide`* — slide number to link to
190
+ - `slide`* — 0-based slide index to link to
188
191
  - `reference` / `reference_link` / `references`
189
192
 
190
193
  ## Common Patterns
@@ -269,6 +272,8 @@ Images in `center`, `split`, and `split-wide` templates automatically get lightb
269
272
  | `/research-topic` | Research a topic for data-driven slides |
270
273
  | `/create-outline` | Create a structured outline before generating YAML |
271
274
  | `/refine-slides` | Review and improve an existing presentation |
275
+ | `/add-template` | Create a custom slide template |
276
+ | `/add-theme` | Create a custom CSS theme |
272
277
 
273
278
  ## Build Commands
274
279
 
@@ -27,3 +27,4 @@ Add a new folder under `presentations/` with an `index.md` file containing your
27
27
 
28
28
  - **Themes** — Drop CSS files into `themes/` to override the default theme
29
29
  - **Styles** — Add custom CSS to `src/styles.css`
30
+ - **Templates** — Run `/add-template` in Claude Code to create custom slide templates
@@ -0,0 +1,74 @@
1
+ # Add Template
2
+
3
+ You are helping the user create a custom slide template for their LazySlides project.
4
+
5
+ ## Step 1: Gather Requirements
6
+
7
+ Ask the user:
8
+ - What should the template do? (purpose, layout idea)
9
+ - What content fields does it need? (titles, text, images, lists, etc.)
10
+ - Any specific layout or styling requirements?
11
+
12
+ ## Step 2: Choose a Name
13
+
14
+ Help the user pick a kebab-case name for the template (e.g. `icon-grid`, `two-images`, `photo-grid`). The name must not conflict with the 17 built-in templates: `title`, `section`, `content`, `center`, `hero`, `metrics`, `comparison`, `columns`, `quote`, `image-overlay`, `code`, `timeline`, `funnel`, `split`, `split-wide`, `table`, `agenda`.
15
+
16
+ ## Step 3: Pick a Starting Point
17
+
18
+ Read the existing templates in `_includes/slides/` and suggest the closest built-in template as a base to adapt from. Show the user the docblock and structure of the suggested template. They can also choose to start from scratch.
19
+
20
+ ## Step 4: Create the Template File
21
+
22
+ Generate `_includes/slides/<name>.njk` following the standard pattern:
23
+
24
+ ```nunjucks
25
+ {#
26
+ TEMPLATE: <name> — <short description>
27
+
28
+ Fields:
29
+ - field_name* — description (required)
30
+ - field_name — description (optional)
31
+
32
+ Usage:
33
+ - template: <name>
34
+ field_name: "value"
35
+ #}
36
+
37
+ <section class="slide-<name>">
38
+ <div class="slide-body">
39
+ {% if slide.title %}
40
+ <div class="slide-header">
41
+ <h2>{{ slide.title }}</h2>
42
+ </div>
43
+ {% endif %}
44
+
45
+ <div class="slide-content">
46
+ {# Template-specific content here #}
47
+ </div>
48
+ </div>
49
+
50
+ {% if slide.notes %}
51
+ <aside class="notes">{{ slide.notes }}</aside>
52
+ {% endif %}
53
+
54
+ {% include "slides/_footer.njk" %}
55
+ </section>
56
+ ```
57
+
58
+ Key conventions to follow:
59
+ - Wrap everything in `<section class="slide-<name>">`
60
+ - Use `slide-body` > `slide-header` + `slide-content` structure
61
+ - Access slide data via `slide.fieldName`
62
+ - Include speaker notes block and footer include
63
+ - Use existing CSS utility classes from the design system where possible
64
+
65
+ ## Step 5: Add CSS
66
+
67
+ If the template needs custom styles, add them to `src/styles.css`. Place them after the existing slide template styles. Use the `.slide-<name>` class as the scope. Follow the existing spacing, font, and color conventions from the design system.
68
+
69
+ ## Step 6: Test It
70
+
71
+ 1. Add a test slide to an existing presentation using `template: <name>` with sample data
72
+ 2. Run `pnpm run validate` — the validator will warn about an unknown template name (this is expected for custom templates)
73
+ 3. Run `pnpm run dev` to preview the result in the browser
74
+ 4. Iterate on the template and styles until it looks right
@@ -0,0 +1,50 @@
1
+ # Add Theme
2
+
3
+ You are helping the user create a custom CSS theme for their LazySlides project.
4
+
5
+ ## Step 1: Gather Brand Requirements
6
+
7
+ Ask the user:
8
+ - What brand or visual identity is this for?
9
+ - Primary accent color (hex code or description like "dark teal")
10
+ - Preferred fonts for headings and body text (or "keep defaults")
11
+ - Footer brand name and optional logo image
12
+ - Any other styling preferences (background gradient, etc.)
13
+
14
+ ## Step 2: Choose a Theme Name
15
+
16
+ Help the user pick a kebab-case name (e.g. `acme-corp`, `dark-blue`, `conference-2026`). This becomes both the filename (`themes/<name>.css`) and the frontmatter value (`theme: <name>`).
17
+
18
+ ## Step 3: Create the Theme File
19
+
20
+ Read `assets/css/themes/default.css` as the reference for all customizable properties. Create `themes/<name>.css` with the user's values. The theme can override any of these:
21
+
22
+ **Typography:**
23
+ - `--font-heading` — headings, quotes, metric values (default: Source Serif 4)
24
+ - `--font-body` — paragraphs, lists, labels, footer (default: Plus Jakarta Sans)
25
+ - Add `@import url(...)` at the top for Google Fonts if needed
26
+
27
+ **Primary color scale:**
28
+ - `--color-primary-50` through `--color-primary-700` — light tints to dark accents
29
+ - Generate a harmonious 7-step scale from the user's chosen color
30
+
31
+ **Footer branding:**
32
+ - `--footer-brand-name` — text shown in footer center
33
+ - `--footer-logo-url` — optional logo URL (e.g. `url('/assets/images/brands/logo.svg')`)
34
+
35
+ **Theater background:**
36
+ - `html, body { background: ... }` — gradient behind the slide cards
37
+
38
+ Only include properties the user wants to change — omitted properties inherit from `src/styles.css` defaults.
39
+
40
+ ## Step 4: Apply the Theme
41
+
42
+ Update the target presentation's `index.md` frontmatter to use the new theme:
43
+
44
+ ```yaml
45
+ theme: <name>
46
+ ```
47
+
48
+ ## Step 5: Preview
49
+
50
+ Run `pnpm run dev` and open the presentation in the browser to review. Iterate on colors, fonts, and spacing until the user is satisfied.
package/src/styles.css CHANGED
@@ -171,6 +171,19 @@ html, body {
171
171
  overflow: hidden;
172
172
  }
173
173
 
174
+ /* Overview mode (ESC key) — ensure all slides are visible as thumbnails.
175
+ Reveal.js sets the hidden attribute on non-present sections, and
176
+ Tailwind's @layer base includes [hidden] { display: none !important }.
177
+ With CSS layers, layered !important beats unlayered !important, so we
178
+ must place our override inside @layer base where specificity decides. */
179
+ @layer base {
180
+ .reveal.overview .slides > section {
181
+ display: flex !important;
182
+ flex-direction: column;
183
+ opacity: 1 !important;
184
+ }
185
+ }
186
+
174
187
  /* ============================================
175
188
  BASE TYPOGRAPHY
176
189
  ============================================ */
@@ -1555,7 +1568,8 @@ html, body {
1555
1568
  }
1556
1569
 
1557
1570
  /* Enlarge progress bar when hovering near bottom */
1558
- .reveal:has(.section-progress-dots:hover) .progress {
1571
+ .reveal:has(.section-progress-dots:hover) .progress,
1572
+ .reveal:has(.progress:hover) .progress {
1559
1573
  height: 20px; /* 5x the original 4px */
1560
1574
  }
1561
1575
 
@@ -2022,7 +2036,7 @@ html, body {
2022
2036
  right: 0;
2023
2037
  height: 40px; /* Large hover zone for easier mouse targeting */
2024
2038
  z-index: 11; /* Above progress bar (z-index: 10) */
2025
- pointer-events: auto; /* Enable hover detection on container */
2039
+ pointer-events: none; /* Let clicks pass through to progress bar */
2026
2040
  }
2027
2041
 
2028
2042
  .section-progress-dots .section-dot {
@@ -2039,7 +2053,8 @@ html, body {
2039
2053
  }
2040
2054
 
2041
2055
  /* Enlarge dots when hovering near bottom */
2042
- .section-progress-dots:hover .section-dot {
2056
+ .section-progress-dots:hover .section-dot,
2057
+ .reveal:has(.progress:hover) .section-progress-dots .section-dot {
2043
2058
  width: 20px;
2044
2059
  height: 20px;
2045
2060
  }