lazyslides 0.2.1 → 0.2.2
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 +1 -1
- package/_includes/slides/_nested-list.njk +28 -12
- package/_includes/slides/_section-attrs.njk +15 -0
- package/_includes/slides/agenda.njk +2 -1
- package/_includes/slides/center.njk +2 -1
- package/_includes/slides/code.njk +2 -1
- package/_includes/slides/columns.njk +4 -3
- package/_includes/slides/comparison.njk +3 -2
- package/_includes/slides/content.njk +4 -3
- package/_includes/slides/funnel.njk +3 -2
- package/_includes/slides/hero.njk +2 -1
- package/_includes/slides/image-overlay.njk +3 -5
- package/_includes/slides/metrics.njk +3 -2
- package/_includes/slides/quote.njk +2 -1
- package/_includes/slides/section.njk +2 -1
- package/_includes/slides/split-wide.njk +4 -3
- package/_includes/slides/split.njk +4 -3
- package/_includes/slides/table.njk +2 -1
- package/_includes/slides/three-columns.njk +48 -0
- package/_includes/slides/timeline.njk +3 -2
- package/_includes/slides/title.njk +2 -1
- package/_layouts/presentation.njk +38 -13
- package/assets/css/themes/card.css +63 -0
- package/assets/css/themes/corporate.css +25 -0
- package/assets/css/themes/default.css +2 -4
- package/assets/css/themes/forest.css +23 -0
- package/assets/css/themes/midnight.css +98 -0
- package/assets/css/themes/sunset.css +23 -0
- package/assets/reveal.js/plugin/math/katex.js +96 -0
- package/assets/reveal.js/plugin/math/math.esm.js +6 -0
- package/assets/reveal.js/plugin/math/math.js +1 -0
- package/assets/reveal.js/plugin/math/mathjax2.js +89 -0
- package/assets/reveal.js/plugin/math/mathjax3.js +77 -0
- package/assets/reveal.js/plugin/math/plugin.js +15 -0
- package/assets/reveal.js/plugin/search/plugin.js +243 -0
- package/assets/reveal.js/plugin/search/search.esm.js +7 -0
- package/assets/reveal.js/plugin/search/search.js +7 -0
- package/assets/reveal.js/plugin/zoom/plugin.js +264 -0
- package/assets/reveal.js/plugin/zoom/zoom.esm.js +11 -0
- package/assets/reveal.js/plugin/zoom/zoom.js +11 -0
- package/index.js +26 -10
- package/lib/validate.js +74 -9
- package/package.json +1 -1
- package/src/styles.css +262 -118
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/presentations/
|
|
13
|
+
[Live demo](https://chrisissorry.github.io/lazyslides/presentations/)
|
|
14
14
|
|
|
15
15
|
## Why
|
|
16
16
|
|
|
@@ -1,22 +1,38 @@
|
|
|
1
|
-
{# Reusable macro for rendering nested lists (map syntax + legacy object syntax)
|
|
2
|
-
|
|
1
|
+
{# Reusable macro for rendering nested lists (map syntax + legacy object syntax).
|
|
2
|
+
Supports fragment animations and auto-animate data-id attributes.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
items — the list items array
|
|
6
|
+
ordered — true for <ol>, false for <ul> (default: false)
|
|
7
|
+
fragment_type — slide-level fragment class applied to all items (e.g. "fade-up")
|
|
8
|
+
#}
|
|
9
|
+
{% macro renderItems(items, ordered, fragment_type) %}
|
|
3
10
|
<{{ "ol" if ordered else "ul" }}>
|
|
4
11
|
{% for item in items %}
|
|
5
|
-
<li
|
|
6
|
-
{
|
|
7
|
-
{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
<li
|
|
13
|
+
{%- if item is mapping and item.text is defined %}
|
|
14
|
+
{%- set frag = item.fragment or fragment_type %}
|
|
15
|
+
{%- if frag %} class="fragment {{ frag }}"{% endif %}
|
|
16
|
+
{%- if item.data_id %} data-id="{{ item.data_id }}"{% endif %}
|
|
17
|
+
{%- elif item is mapping %}
|
|
18
|
+
{%- if fragment_type %} class="fragment {{ fragment_type }}"{% endif %}
|
|
19
|
+
{%- else %}
|
|
20
|
+
{%- if fragment_type %} class="fragment {{ fragment_type }}"{% endif %}
|
|
21
|
+
{%- endif %}>
|
|
22
|
+
{% if item is mapping and item.text is defined %}
|
|
14
23
|
{{ item.text }}
|
|
15
24
|
{% if item.sub_items %}
|
|
16
25
|
<ul>
|
|
17
|
-
{% for sub in item.sub_items %}<li>{{ sub }}</li>{% endfor %}
|
|
26
|
+
{% for sub in item.sub_items %}<li{% if frag %} class="fragment {{ frag }}"{% endif %}>{{ sub }}</li>{% endfor %}
|
|
18
27
|
</ul>
|
|
19
28
|
{% endif %}
|
|
29
|
+
{% elif item is mapping %}
|
|
30
|
+
{% for key, children in item %}
|
|
31
|
+
{{ key }}
|
|
32
|
+
<ul>
|
|
33
|
+
{% for sub in children %}<li{% if fragment_type %} class="fragment {{ fragment_type }}"{% endif %}>{{ sub }}</li>{% endfor %}
|
|
34
|
+
</ul>
|
|
35
|
+
{% endfor %}
|
|
20
36
|
{% else %}
|
|
21
37
|
{{ item }}
|
|
22
38
|
{% endif %}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{# Reusable macro for emitting data-* attributes on <section> tags.
|
|
2
|
+
Handles transitions, auto-animate, backgrounds (color, gradient, image, video). #}
|
|
3
|
+
{% macro sectionAttrs(slide) %}
|
|
4
|
+
{%- if slide.transition %} data-transition="{{ slide.transition }}"{% endif %}
|
|
5
|
+
{%- if slide.transition_speed %} data-transition-speed="{{ slide.transition_speed }}"{% endif %}
|
|
6
|
+
{%- if slide.auto_animate %} data-auto-animate{% endif %}
|
|
7
|
+
{%- if slide.auto_animate_easing %} data-auto-animate-easing="{{ slide.auto_animate_easing }}"{% endif %}
|
|
8
|
+
{%- if slide.auto_animate_duration %} data-auto-animate-duration="{{ slide.auto_animate_duration }}"{% endif %}
|
|
9
|
+
{%- if slide.background_color %} data-background-color="{{ slide.background_color }}"{% endif %}
|
|
10
|
+
{%- if slide.background_gradient %} data-background="{{ slide.background_gradient }}"{% endif %}
|
|
11
|
+
{%- if slide.background_image %} data-background-image="{{ slide.background_image }}"{% endif %}
|
|
12
|
+
{%- if slide.background_video %} data-background-video="{{ slide.background_video }}"{% endif %}
|
|
13
|
+
{%- if slide.background_video_loop %} data-background-video-loop{% endif %}
|
|
14
|
+
{%- if slide.background_video_muted %} data-background-video-muted{% endif %}
|
|
15
|
+
{%- endmacro %}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{# TEMPLATE: agenda — Clickable table of contents for presentation sections.
|
|
2
2
|
Set auto_generate: true to build the agenda automatically from section slides.
|
|
3
3
|
Manual mode: provide sections array with title and slide (0-based index). #}
|
|
4
|
-
|
|
4
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
5
|
+
<section class="slide-agenda"{{ sectionAttrs(slide) }}>
|
|
5
6
|
<div class="slide-body">
|
|
6
7
|
<div class="slide-header">
|
|
7
8
|
<h2>{{ slide.title | default("Agenda") }}</h2>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: center — Centered statement, image, or both. Image has lightbox on click. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-center"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
{% if slide.title %}<h2>{{ slide.title }}</h2>{% endif %}
|
|
5
6
|
{% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: code — Code snippet display with syntax highlighting. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-code"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{# TEMPLATE: columns — Two-column layout for side-by-side content. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-columns"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
6
7
|
</div>
|
|
7
8
|
<div class="columns">
|
|
8
|
-
<div class="column">
|
|
9
|
+
<div class="column{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
9
10
|
{% if slide.left_title %}<h3>{{ slide.left_title }}</h3>{% endif %}
|
|
10
11
|
{% if slide.left_text %}<p>{{ slide.left_text }}</p>{% endif %}
|
|
11
12
|
{% if slide.left_items %}
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
</ul>
|
|
16
17
|
{% endif %}
|
|
17
18
|
</div>
|
|
18
|
-
<div class="column">
|
|
19
|
+
<div class="column{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
19
20
|
{% if slide.right_title %}<h3>{{ slide.right_title }}</h3>{% endif %}
|
|
20
21
|
{% if slide.right_text %}<p>{{ slide.right_text }}</p>{% endif %}
|
|
21
22
|
{% if slide.right_items %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: comparison — Before/after two-column table with optional highlight styling. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-comparison{% if slide.highlight == 'left' %} highlight-left{% elif slide.highlight == 'none' %} highlight-none{% endif %}"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
</thead>
|
|
14
15
|
<tbody>
|
|
15
16
|
{% for row in slide.rows %}
|
|
16
|
-
<tr>
|
|
17
|
+
<tr{% if slide.fragment %} class="fragment {{ slide.fragment }}"{% endif %}>
|
|
17
18
|
<td>{% if row.left_icon == "bad" %}<span class="icon-bad">✗</span>{% endif %}{{ row.left }}</td>
|
|
18
19
|
<td>{% if row.right_icon == "good" %}<span class="icon-good">✓</span>{% endif %}{{ row.right }}</td>
|
|
19
20
|
</tr>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{# TEMPLATE: content — Standard slide with headline and bullet points. #}
|
|
2
2
|
{% from "slides/_nested-list.njk" import renderItems %}
|
|
3
|
-
|
|
3
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
4
|
+
<section class="slide-content"{{ sectionAttrs(slide) }}>
|
|
4
5
|
<div class="slide-body">
|
|
5
6
|
<div class="slide-header">
|
|
6
7
|
<h2>{{ slide.title }}</h2>
|
|
@@ -8,10 +9,10 @@
|
|
|
8
9
|
</div>
|
|
9
10
|
{% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
|
|
10
11
|
{% if slide.items %}
|
|
11
|
-
{{ renderItems(slide.items) }}
|
|
12
|
+
{{ renderItems(slide.items, false, slide.fragment) }}
|
|
12
13
|
{% endif %}
|
|
13
14
|
{% if slide.ordered_items %}
|
|
14
|
-
{{ renderItems(slide.ordered_items, true) }}
|
|
15
|
+
{{ renderItems(slide.ordered_items, true, slide.fragment) }}
|
|
15
16
|
{% endif %}
|
|
16
17
|
{% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
|
|
17
18
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: funnel — Progressive narrowing funnel with stage labels and annotations. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-funnel"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
<div class="funnel-wrapper">
|
|
8
9
|
<div class="funnel-grid">
|
|
9
10
|
{% for stage in slide.stages %}
|
|
10
|
-
<div class="funnel-row">
|
|
11
|
+
<div class="funnel-row{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
11
12
|
<div class="funnel-stage-wrapper">
|
|
12
13
|
<div class="funnel-stage stage-{{ loop.index }}">
|
|
13
14
|
<span class="stage-label">{{ stage.label }}</span>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{# TEMPLATE: hero — Full-bleed background image with dark overlay and optional text box. #}
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
2
3
|
{% if slide.image %}{% set img_src = slide.image | resolveImage %}{% endif %}
|
|
3
|
-
<section class="slide-hero"{% if slide.image %}
|
|
4
|
+
<section class="slide-hero"{% if slide.image %} data-background-image="{{ img_src }}" data-background-size="cover" data-background-position="center"{% elif slide.color %} data-background-color="{{ slide.color }}"{% endif %}{{ sectionAttrs(slide) }}>
|
|
4
5
|
<div class="slide-body">
|
|
5
6
|
<h1>{{ slide.title }}</h1>
|
|
6
7
|
{% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
{# TEMPLATE: image-overlay — Full background image with positioned text box overlay.
|
|
2
|
-
|
|
3
|
-
Reveal.js data-background-* renders in the full viewport (including theater margins),
|
|
4
|
-
while inline style keeps the image within the 960×540 slide bounds. #}
|
|
1
|
+
{# TEMPLATE: image-overlay — Full background image with positioned text box overlay. #}
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
5
3
|
{% set img_src = slide.image | resolveImage %}
|
|
6
|
-
<section class="slide-image-overlay"
|
|
4
|
+
<section class="slide-image-overlay" data-background-image="{{ img_src }}" data-background-size="cover" data-background-position="center"{{ sectionAttrs(slide) }}>
|
|
7
5
|
<div class="slide-body">
|
|
8
6
|
<div class="overlay-box {{ slide.position | default('bottom-left') }}">
|
|
9
7
|
{% if slide.title %}<h3>{{ slide.title }}</h3>{% endif %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: metrics — Key statistics display with 2-4 metric cards. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-metrics"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
{% else %}{% set cols_class = "cols-3" %}{% endif %}
|
|
12
13
|
<div class="metrics-grid {{ cols_class }}">
|
|
13
14
|
{% for metric in slide.metrics %}
|
|
14
|
-
<div class="metric-card">
|
|
15
|
+
<div class="metric-card{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
15
16
|
<div class="value{% if metric.color %} {{ metric.color }}{% endif %}">{{ metric.value }}</div>
|
|
16
17
|
<div class="label">{{ metric.label }}</div>
|
|
17
18
|
{% if metric.context %}<div class="context">{{ metric.context }}</div>{% endif %}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: quote — Testimonial or statement with attribution. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-quote"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="quote-mark">"</div>
|
|
5
6
|
<blockquote>{{ slide.quote }}</blockquote>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: section — Chapter divider slide for separating presentation sections. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-section"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<h2>{{ slide.title }}</h2>
|
|
4
5
|
{% if slide.subtitle %}<p class="subtitle">{{ slide.subtitle }}</p>{% endif %}
|
|
5
6
|
{% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{# TEMPLATE: split-wide — 1/3 content + 2/3 image layout (inverse of split). #}
|
|
2
2
|
{% from "slides/_nested-list.njk" import renderItems %}
|
|
3
|
-
|
|
3
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
4
|
+
<section class="slide-split-wide"{{ sectionAttrs(slide) }}>
|
|
4
5
|
<div class="slide-body">
|
|
5
6
|
<div class="split-wide-container">
|
|
6
7
|
<div class="split-wide-content">
|
|
@@ -9,11 +10,11 @@
|
|
|
9
10
|
</div>
|
|
10
11
|
{% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
|
|
11
12
|
{% if slide.items %}
|
|
12
|
-
{{ renderItems(slide.items) }}
|
|
13
|
+
{{ renderItems(slide.items, false, slide.fragment) }}
|
|
13
14
|
{% endif %}
|
|
14
15
|
{% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
|
|
15
16
|
</div>
|
|
16
|
-
<div class="split-wide-image">
|
|
17
|
+
<div class="split-wide-image{% if slide.image_fit == 'contain' %} image-contain{% endif %}">
|
|
17
18
|
{% if slide.image %}
|
|
18
19
|
{% set img_src = slide.image | resolveImage %}
|
|
19
20
|
<a href="{{ img_src }}" class="glightbox">
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{# TEMPLATE: split — 1/3 image + 2/3 content layout. Image has lightbox on click. #}
|
|
2
2
|
{% from "slides/_nested-list.njk" import renderItems %}
|
|
3
|
-
|
|
3
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
4
|
+
<section class="slide-split"{{ sectionAttrs(slide) }}>
|
|
4
5
|
<div class="slide-body">
|
|
5
6
|
<div class="split-container">
|
|
6
|
-
<div class="split-image">
|
|
7
|
+
<div class="split-image{% if slide.image_fit == 'contain' %} image-contain{% endif %}">
|
|
7
8
|
{% if slide.image %}
|
|
8
9
|
{% set img_src = slide.image | resolveImage %}
|
|
9
10
|
<a href="{{ img_src }}" class="glightbox">
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
</div>
|
|
18
19
|
{% if slide.text %}<p>{{ slide.text }}</p>{% endif %}
|
|
19
20
|
{% if slide.items %}
|
|
20
|
-
{{ renderItems(slide.items) }}
|
|
21
|
+
{{ renderItems(slide.items, false, slide.fragment) }}
|
|
21
22
|
{% endif %}
|
|
22
23
|
{% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
|
|
23
24
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: table — Multi-column data table with neutral styling and zebra striping. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-table"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{# TEMPLATE: three-columns — Three equal-width columns for side-by-side content. #}
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-three-columns"{{ sectionAttrs(slide) }}>
|
|
4
|
+
<div class="slide-body">
|
|
5
|
+
<div class="slide-header">
|
|
6
|
+
<h2>{{ slide.title }}</h2>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="three-columns">
|
|
9
|
+
<div class="column{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
10
|
+
{% if slide.col1_title %}<h3>{{ slide.col1_title }}</h3>{% endif %}
|
|
11
|
+
{% if slide.col1_text %}<p>{{ slide.col1_text }}</p>{% endif %}
|
|
12
|
+
{% if slide.col1_items %}
|
|
13
|
+
<ul>
|
|
14
|
+
{% for item in slide.col1_items %}<li>{{ item }}</li>
|
|
15
|
+
{% endfor %}
|
|
16
|
+
</ul>
|
|
17
|
+
{% endif %}
|
|
18
|
+
</div>
|
|
19
|
+
<div class="column{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
20
|
+
{% if slide.col2_title %}<h3>{{ slide.col2_title }}</h3>{% endif %}
|
|
21
|
+
{% if slide.col2_text %}<p>{{ slide.col2_text }}</p>{% endif %}
|
|
22
|
+
{% if slide.col2_items %}
|
|
23
|
+
<ul>
|
|
24
|
+
{% for item in slide.col2_items %}<li>{{ item }}</li>
|
|
25
|
+
{% endfor %}
|
|
26
|
+
</ul>
|
|
27
|
+
{% endif %}
|
|
28
|
+
</div>
|
|
29
|
+
<div class="column{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
30
|
+
{% if slide.col3_title %}<h3>{{ slide.col3_title }}</h3>{% endif %}
|
|
31
|
+
{% if slide.col3_text %}<p>{{ slide.col3_text }}</p>{% endif %}
|
|
32
|
+
{% if slide.col3_items %}
|
|
33
|
+
<ul>
|
|
34
|
+
{% for item in slide.col3_items %}<li>{{ item }}</li>
|
|
35
|
+
{% endfor %}
|
|
36
|
+
</ul>
|
|
37
|
+
{% endif %}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
{% if slide.notes %}<aside class="notes">{{ slide.notes }}</aside>{% endif %}
|
|
41
|
+
</div>
|
|
42
|
+
{% if global_footer and not slide.hide_footer %}
|
|
43
|
+
{% set reference = slide.reference %}
|
|
44
|
+
{% set reference_link = slide.reference_link %}
|
|
45
|
+
{% set references = slide.references %}
|
|
46
|
+
{% include "slides/footer.njk" %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
</section>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: timeline — Horizontal timeline with alternating above/below event cards. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-timeline"{{ sectionAttrs(slide) }}>
|
|
3
4
|
<div class="slide-body">
|
|
4
5
|
<div class="slide-header">
|
|
5
6
|
<h2>{{ slide.title }}</h2>
|
|
@@ -9,7 +10,7 @@
|
|
|
9
10
|
<div class="timeline-events">
|
|
10
11
|
{% for event in slide.events %}
|
|
11
12
|
{% set remainder = loop.index % 2 %}
|
|
12
|
-
<div class="timeline-event {% if remainder == 1 %}above{% else %}below{% endif %}">
|
|
13
|
+
<div class="timeline-event {% if remainder == 1 %}above{% else %}below{% endif %}{% if slide.fragment %} fragment {{ slide.fragment }}{% endif %}">
|
|
13
14
|
<div class="event-dot"></div>
|
|
14
15
|
<div class="event-connector"></div>
|
|
15
16
|
<div class="event-card">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{# TEMPLATE: title — Opening slide with logo, title, subtitle, and author. #}
|
|
2
|
-
|
|
2
|
+
{% from "slides/_section-attrs.njk" import sectionAttrs %}
|
|
3
|
+
<section class="slide-title"{{ sectionAttrs(slide) }}>
|
|
3
4
|
{% if slide.logo != false %}
|
|
4
5
|
<div class="logo">
|
|
5
6
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -17,10 +17,17 @@
|
|
|
17
17
|
<!-- Tailwind CSS (structural styles) -->
|
|
18
18
|
<link rel="stylesheet" href="{{ '/assets/css/styles.css' | url }}">
|
|
19
19
|
|
|
20
|
-
<!-- Theme
|
|
20
|
+
<!-- Theme -->
|
|
21
21
|
{% set theme_name = theme | default('default') %}
|
|
22
22
|
<link rel="stylesheet" href="{{ '/assets/css/themes/' | url }}{{ theme_name }}.css">
|
|
23
23
|
|
|
24
|
+
{% if footer_brand or footer_logo == false %}
|
|
25
|
+
<style>
|
|
26
|
+
{% if footer_brand %}:root { --footer-brand-name: '{{ footer_brand }}'; }{% endif %}
|
|
27
|
+
{% if footer_logo == false %}.footer-center .logo-small { display: none; }{% endif %}
|
|
28
|
+
</style>
|
|
29
|
+
{% endif %}
|
|
30
|
+
|
|
24
31
|
<!-- Code highlighting theme -->
|
|
25
32
|
<link rel="stylesheet" href="{{ '/assets/reveal.js/plugin/highlight/monokai.css' | url }}">
|
|
26
33
|
|
|
@@ -28,15 +35,6 @@
|
|
|
28
35
|
<link rel="stylesheet" href="{{ '/assets/css/vendor/glightbox.min.css' | url }}">
|
|
29
36
|
</head>
|
|
30
37
|
<body>
|
|
31
|
-
<!-- Exit button to return to presentations index -->
|
|
32
|
-
<a href="{{ '/presentations/' | url }}"
|
|
33
|
-
class="exit-button"
|
|
34
|
-
title="Exit to presentations list">
|
|
35
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
36
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
37
|
-
</svg>
|
|
38
|
-
</a>
|
|
39
|
-
|
|
40
38
|
<div class="reveal">
|
|
41
39
|
<div class="slides">
|
|
42
40
|
{% for slide in slides %}
|
|
@@ -52,6 +50,9 @@
|
|
|
52
50
|
<div class="section-progress-dots"></div>
|
|
53
51
|
</div>
|
|
54
52
|
|
|
53
|
+
<!-- Viewport footer — fixed at screen bottom, outside Reveal.js canvas -->
|
|
54
|
+
<div id="viewport-footer"></div>
|
|
55
|
+
|
|
55
56
|
<!-- Reveal.js -->
|
|
56
57
|
<script src="{{ '/assets/reveal.js/dist/reveal.js' | url }}"></script>
|
|
57
58
|
|
|
@@ -59,6 +60,11 @@
|
|
|
59
60
|
<script src="{{ '/assets/reveal.js/plugin/markdown/markdown.js' | url }}"></script>
|
|
60
61
|
<script src="{{ '/assets/reveal.js/plugin/highlight/highlight.js' | url }}"></script>
|
|
61
62
|
<script src="{{ '/assets/reveal.js/plugin/notes/notes.js' | url }}"></script>
|
|
63
|
+
<script src="{{ '/assets/reveal.js/plugin/zoom/zoom.js' | url }}"></script>
|
|
64
|
+
<script src="{{ '/assets/reveal.js/plugin/search/search.js' | url }}"></script>
|
|
65
|
+
{% if math %}
|
|
66
|
+
<script src="{{ '/assets/reveal.js/plugin/math/math.js' | url }}"></script>
|
|
67
|
+
{% endif %}
|
|
62
68
|
|
|
63
69
|
<!-- Convert newlines to <br/> in speaker notes for proper line breaks -->
|
|
64
70
|
<script>
|
|
@@ -81,7 +87,7 @@
|
|
|
81
87
|
height: {{ height | default(540) }},
|
|
82
88
|
|
|
83
89
|
// Scale settings
|
|
84
|
-
margin:
|
|
90
|
+
margin: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--reveal-margin')) || 0,
|
|
85
91
|
minScale: 0.2,
|
|
86
92
|
maxScale: 2.0,
|
|
87
93
|
|
|
@@ -94,7 +100,7 @@
|
|
|
94
100
|
progress: isPdfExport ? false : {{ progress | default(true) }},
|
|
95
101
|
|
|
96
102
|
// Slide number
|
|
97
|
-
slideNumber:
|
|
103
|
+
slideNumber: false,
|
|
98
104
|
|
|
99
105
|
// Push each slide to browser history
|
|
100
106
|
history: {{ history | default(true) }},
|
|
@@ -157,7 +163,7 @@
|
|
|
157
163
|
mobileViewDistance: {{ mobileViewDistance | default(2) }},
|
|
158
164
|
|
|
159
165
|
// Plugins
|
|
160
|
-
plugins: [ RevealMarkdown, RevealHighlight, RevealNotes ]
|
|
166
|
+
plugins: [ RevealMarkdown, RevealHighlight, RevealNotes, RevealZoom, RevealSearch{% if math %}, RevealMath.KaTeX{% endif %} ]
|
|
161
167
|
});
|
|
162
168
|
</script>
|
|
163
169
|
|
|
@@ -237,5 +243,24 @@
|
|
|
237
243
|
updateDots(); // Set initial state
|
|
238
244
|
});
|
|
239
245
|
</script>
|
|
246
|
+
|
|
247
|
+
<!-- Viewport footer: clone current slide's footer to fixed container -->
|
|
248
|
+
<script>
|
|
249
|
+
function updateViewportFooter() {
|
|
250
|
+
const container = document.getElementById('viewport-footer');
|
|
251
|
+
if (!container || document.body.classList.contains('pdf-export')) return;
|
|
252
|
+
|
|
253
|
+
const currentSlide = Reveal.getCurrentSlide();
|
|
254
|
+
const footer = currentSlide?.querySelector('.slide-footer');
|
|
255
|
+
|
|
256
|
+
container.innerHTML = '';
|
|
257
|
+
if (footer) {
|
|
258
|
+
container.appendChild(footer.cloneNode(true));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
Reveal.on('ready', updateViewportFooter);
|
|
263
|
+
Reveal.on('slidechanged', updateViewportFooter);
|
|
264
|
+
</script>
|
|
240
265
|
</body>
|
|
241
266
|
</html>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
CARD THEME
|
|
3
|
+
Restores the card/theater aesthetic where slides
|
|
4
|
+
appear as floating white cards on a gradient background.
|
|
5
|
+
|
|
6
|
+
Usage: Add `theme: card` to presentation frontmatter
|
|
7
|
+
============================================ */
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
/* Primary Color Scale — same sky blue as default */
|
|
11
|
+
--color-primary-50: #f0f9ff;
|
|
12
|
+
--color-primary-100: #e0f2fe;
|
|
13
|
+
--color-primary-200: #bae6fd;
|
|
14
|
+
--color-primary-400: #38bdf8;
|
|
15
|
+
--color-primary-500: #0ea5e9;
|
|
16
|
+
--color-primary-600: #0284c7;
|
|
17
|
+
--color-primary-700: #0369a1;
|
|
18
|
+
|
|
19
|
+
--reveal-margin: 0.04; /* Theater-style inset for card frame */
|
|
20
|
+
--footer-brand-name: 'lazyslides';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Theater gradient background */
|
|
24
|
+
html, body {
|
|
25
|
+
background: linear-gradient(135deg, #f1f5f9 0%, var(--color-primary-50) 100%);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Transparent viewport so theater background shows through */
|
|
29
|
+
.reveal-viewport {
|
|
30
|
+
background: transparent !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* White card styling on each slide */
|
|
34
|
+
.reveal .slides > section,
|
|
35
|
+
.reveal .slides > section > section {
|
|
36
|
+
background: #ffffff;
|
|
37
|
+
border-radius: 6px;
|
|
38
|
+
border: 1px solid var(--color-slate-200, #e2e8f0);
|
|
39
|
+
box-shadow:
|
|
40
|
+
0 2px 3px -1px rgba(0, 0, 0, 0.07),
|
|
41
|
+
0 5px 10px -3px rgba(0, 0, 0, 0.05);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Restore footer rounded corners */
|
|
45
|
+
.slide-footer {
|
|
46
|
+
border-radius: 0 0 6px 6px;
|
|
47
|
+
border-top: 1px solid var(--color-slate-200);
|
|
48
|
+
background: var(--color-slate-50);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Hero and image-overlay get rounded corners too */
|
|
52
|
+
.reveal .slides section.slide-hero,
|
|
53
|
+
.reveal .slides section.slide-image-overlay {
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* PDF export removes card styling */
|
|
58
|
+
.pdf-export .reveal .slides > section,
|
|
59
|
+
.pdf-export .reveal .slides > section > section {
|
|
60
|
+
border-radius: 0;
|
|
61
|
+
border: none;
|
|
62
|
+
box-shadow: none;
|
|
63
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
CORPORATE THEME
|
|
3
|
+
Charcoal and slate, minimal and professional.
|
|
4
|
+
|
|
5
|
+
Usage: Add `theme: corporate` to presentation frontmatter
|
|
6
|
+
============================================ */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
/* Primary Color Scale — slate/charcoal */
|
|
10
|
+
--color-primary-50: #f8fafc;
|
|
11
|
+
--color-primary-100: #f1f5f9;
|
|
12
|
+
--color-primary-200: #e2e8f0;
|
|
13
|
+
--color-primary-400: #94a3b8;
|
|
14
|
+
--color-primary-500: #64748b;
|
|
15
|
+
--color-primary-600: #475569;
|
|
16
|
+
--color-primary-700: #334155;
|
|
17
|
+
|
|
18
|
+
--font-heading: var(--font-sans);
|
|
19
|
+
|
|
20
|
+
--footer-brand-name: 'lazyslides';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
html, body {
|
|
24
|
+
background: #ffffff;
|
|
25
|
+
}
|
|
@@ -52,11 +52,9 @@
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/* ----------------------------------------
|
|
55
|
-
|
|
56
|
-
The gradient behind the slide cards.
|
|
55
|
+
BACKGROUND
|
|
57
56
|
---------------------------------------- */
|
|
58
57
|
|
|
59
58
|
html, body {
|
|
60
|
-
|
|
61
|
-
background: linear-gradient(135deg, #f1f5f9 0%, var(--color-primary-50) 100%);
|
|
59
|
+
background: var(--color-background, #ffffff);
|
|
62
60
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* ============================================
|
|
2
|
+
FOREST THEME
|
|
3
|
+
Deep greens with warm earth tones.
|
|
4
|
+
|
|
5
|
+
Usage: Add `theme: forest` to presentation frontmatter
|
|
6
|
+
============================================ */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
/* Primary Color Scale — forest green */
|
|
10
|
+
--color-primary-50: #f0fdf4;
|
|
11
|
+
--color-primary-100: #dcfce7;
|
|
12
|
+
--color-primary-200: #bbf7d0;
|
|
13
|
+
--color-primary-400: #4ade80;
|
|
14
|
+
--color-primary-500: #22c55e;
|
|
15
|
+
--color-primary-600: #16a34a;
|
|
16
|
+
--color-primary-700: #15803d;
|
|
17
|
+
|
|
18
|
+
--footer-brand-name: 'lazyslides';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
html, body {
|
|
22
|
+
background: #fafdf7;
|
|
23
|
+
}
|