@cyber-dash-tech/revela 0.1.2 → 0.1.4
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 +3 -4
- package/README.zh-CN.md +3 -4
- package/lib/agents/research-prompt.ts +7 -3
- package/lib/config.ts +1 -1
- package/lib/design/designs.ts +97 -7
- package/lib/log.ts +3 -2
- package/lib/prompt-builder.ts +29 -50
- package/lib/qa/checks.ts +6 -49
- package/lib/qa/measure.ts +8 -7
- package/package.json +1 -1
- package/plugin.ts +15 -14
- package/skill/SKILL.md +23 -198
- package/tools/designs.ts +21 -5
- package/tools/workspace-scan.ts +17 -2
- package/designs/default/DESIGN.md +0 -1100
- package/designs/editorial-ribbon/DESIGN.md +0 -1092
- package/designs/minimal/DESIGN.md +0 -1079
|
@@ -1,1079 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: minimal
|
|
3
|
-
description: Warm off-white light theme — frosted-glass cards, typographic, distraction-free
|
|
4
|
-
author: slides-it
|
|
5
|
-
version: 1.0.0
|
|
6
|
-
preview: bundled
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Visual Style — Minimal Theme
|
|
10
|
-
|
|
11
|
-
Apply this visual style when generating all slides in this session.
|
|
12
|
-
|
|
13
|
-
This theme pairs a warm, off-white background with frosted-glass cards and subtle geometric depth. Think: a well-designed printed magazine with a modern glass layer — warm paper feel, elevated with translucency.
|
|
14
|
-
|
|
15
|
-
<!-- @section:global:start -->
|
|
16
|
-
|
|
17
|
-
### Color Palette (Unchanged)
|
|
18
|
-
|
|
19
|
-
```css
|
|
20
|
-
:root {
|
|
21
|
-
--bg-primary: #FAF9F6; /* warm off-white — main background */
|
|
22
|
-
--bg-secondary: #F0EEE8; /* slightly darker warm grey */
|
|
23
|
-
--bg-card: rgba(255, 255, 255, 0.55); /* frosted glass white */
|
|
24
|
-
--text-primary: #1A1916; /* near-black warm ink */
|
|
25
|
-
--text-secondary:#6B6560; /* warm mid-grey */
|
|
26
|
-
--text-muted: #A09890; /* subtle captions */
|
|
27
|
-
--accent: #1A1916; /* same as text — monochrome accent */
|
|
28
|
-
--accent-2: #6B6560; /* secondary accent — warm grey */
|
|
29
|
-
--border: rgba(226, 222, 213, 0.6); /* soft warm border, semi-transparent */
|
|
30
|
-
--border-strong: #C9C4B8; /* stronger divider */
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Typography
|
|
35
|
-
|
|
36
|
-
- **Display font**: `DM Serif Display` (headings) — load from Google Fonts
|
|
37
|
-
- **Body font**: `DM Sans` (body, captions) — load from Google Fonts
|
|
38
|
-
- Font link tag:
|
|
39
|
-
```html
|
|
40
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
41
|
-
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap" rel="stylesheet">
|
|
42
|
-
```
|
|
43
|
-
- Title size (h1): `64px`, weight 400
|
|
44
|
-
- Section heading (h2): `42px`, weight 400
|
|
45
|
-
- Subtitle size: `22px`, weight 300
|
|
46
|
-
- Body size: `18px`
|
|
47
|
-
- Label: `13px`, letter-spacing `0.1em`, uppercase
|
|
48
|
-
- Card title: `24px` (DM Serif Display)
|
|
49
|
-
- Card body: `17px`
|
|
50
|
-
- Stat number: `64px`, weight 400
|
|
51
|
-
- Stat label: `15px`, uppercase
|
|
52
|
-
- Line height: `1.75` for body, `1.15` for h1, `1.2` for h2
|
|
53
|
-
- Letter spacing: `-0.02em` for large headings
|
|
54
|
-
- Card title: weight 400 (DM Serif Display)
|
|
55
|
-
- Card body / body-text: weight 400
|
|
56
|
-
- Stat label: weight 400
|
|
57
|
-
|
|
58
|
-
All sizes are fixed `px` — designed for the 1920×1080 canvas. JS `transform: scale()`
|
|
59
|
-
handles viewport adaptation. **Never use `clamp()` or viewport-relative units.**
|
|
60
|
-
|
|
61
|
-
### Background Layers (Subtle Geometric Depth)
|
|
62
|
-
|
|
63
|
-
**Layer 1 — Base gradient (subtle warmth):**
|
|
64
|
-
```css
|
|
65
|
-
body::before {
|
|
66
|
-
content: '';
|
|
67
|
-
position: fixed;
|
|
68
|
-
inset: 0;
|
|
69
|
-
background: linear-gradient(180deg, #FAF9F6 0%, #F5F3EE 50%, #FAF9F6 100%);
|
|
70
|
-
z-index: -3;
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**Layer 2 — Geometric circles (blurred, warm amber tones):**
|
|
75
|
-
```css
|
|
76
|
-
.geo-shape-1, .geo-shape-2, .geo-shape-3 {
|
|
77
|
-
position: fixed;
|
|
78
|
-
border-radius: 50%;
|
|
79
|
-
filter: blur(60px);
|
|
80
|
-
opacity: 0.20;
|
|
81
|
-
z-index: -2;
|
|
82
|
-
pointer-events: none;
|
|
83
|
-
}
|
|
84
|
-
.geo-shape-1 {
|
|
85
|
-
width: 600px;
|
|
86
|
-
height: 600px;
|
|
87
|
-
background: #FFB347;
|
|
88
|
-
top: -200px;
|
|
89
|
-
right: -100px;
|
|
90
|
-
}
|
|
91
|
-
.geo-shape-2 {
|
|
92
|
-
width: 400px;
|
|
93
|
-
height: 400px;
|
|
94
|
-
background: #FFA633;
|
|
95
|
-
bottom: -100px;
|
|
96
|
-
left: -100px;
|
|
97
|
-
}
|
|
98
|
-
.geo-shape-3 {
|
|
99
|
-
width: 300px;
|
|
100
|
-
height: 300px;
|
|
101
|
-
background: #EE9526;
|
|
102
|
-
top: 50%;
|
|
103
|
-
left: 30%;
|
|
104
|
-
opacity: 0.15;
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
**Layer 3 — Subtle diagonal lines (paper texture):**
|
|
109
|
-
```css
|
|
110
|
-
body::after {
|
|
111
|
-
content: '';
|
|
112
|
-
position: fixed;
|
|
113
|
-
inset: 0;
|
|
114
|
-
background: repeating-linear-gradient(
|
|
115
|
-
45deg,
|
|
116
|
-
transparent,
|
|
117
|
-
transparent 3px,
|
|
118
|
-
rgba(0, 0, 0, 0.008) 3px,
|
|
119
|
-
rgba(0, 0, 0, 0.008) 6px
|
|
120
|
-
);
|
|
121
|
-
z-index: -1;
|
|
122
|
-
pointer-events: none;
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Slide Layout
|
|
127
|
-
|
|
128
|
-
- **1920×1080 fixed canvas** — each slide uses a `.slide-canvas` (1920×1080px),
|
|
129
|
-
scaled to fit the viewport via JS `transform: scale()`.
|
|
130
|
-
- Content width tiers (inside canvas, via `max-width` + `margin: 0 auto`):
|
|
131
|
-
- Default (`1200px`): Cover, Quote, Closing
|
|
132
|
-
- Wide (`1600px`): content-heavy slides (grids, multi-column, process flows)
|
|
133
|
-
- Canvas padding: `60px 80px`
|
|
134
|
-
- **No `clamp()` — use fixed `px` for all sizes.** The canvas is always
|
|
135
|
-
1920×1080 and JS handles scaling, so all typography and spacing must be fixed `px`.
|
|
136
|
-
- **Canvas utilization** — Elements should fill approximately 70–80% of the
|
|
137
|
-
1920×1080 canvas area. Even in a minimal design, slides should not feel empty.
|
|
138
|
-
Use larger serif headings, more generous card heights, wider grids, and
|
|
139
|
-
comfortable padding to occupy the space. The calm feeling comes from typography
|
|
140
|
-
and whitespace *rhythm*, not from leaving the canvas half-empty.
|
|
141
|
-
- **Content slides** (Feature Cards, Stats, Two-Column, Step Flow): grid/flex
|
|
142
|
-
containers should stretch toward the canvas edges. Frosted-glass cards should
|
|
143
|
-
have ample internal padding and body text so they feel substantial.
|
|
144
|
-
- **Sparse slides** (Cover, Quote, Closing): add decorative fills — thin geometric
|
|
145
|
-
rules, positioned warm-amber circles (low opacity), large typographic ornaments
|
|
146
|
-
(e.g. oversized `"` for quotes in `var(--border)` color), or horizontal `1px`
|
|
147
|
-
dividers that span most of the canvas width. These anchor the composition
|
|
148
|
-
without breaking the calm, paper-like feel.
|
|
149
|
-
- **Never** leave more than ~20% of the canvas visually empty on any slide.
|
|
150
|
-
- Title slide: large serif heading left-aligned + thin `1px` rule beneath + muted subtitle
|
|
151
|
-
- Content slides: heading top with `border-bottom: 1px solid var(--border)` separator, content below
|
|
152
|
-
|
|
153
|
-
### HTML Structure
|
|
154
|
-
|
|
155
|
-
Every generated presentation must use this exact HTML skeleton:
|
|
156
|
-
|
|
157
|
-
```html
|
|
158
|
-
<!DOCTYPE html>
|
|
159
|
-
<html lang="{language}">
|
|
160
|
-
<head>
|
|
161
|
-
<meta charset="UTF-8">
|
|
162
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
163
|
-
<title>{Presentation Title}</title>
|
|
164
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
165
|
-
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap" rel="stylesheet">
|
|
166
|
-
<script src="https://cdn.jsdelivr.net/npm/lucide@latest/dist/umd/lucide.js"></script>
|
|
167
|
-
<!-- <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> ← only if charts needed -->
|
|
168
|
-
<style>/* all CSS here */</style>
|
|
169
|
-
</head>
|
|
170
|
-
<body>
|
|
171
|
-
<!-- Geometric background shapes -->
|
|
172
|
-
<div class="geo-shape-1"></div>
|
|
173
|
-
<div class="geo-shape-2"></div>
|
|
174
|
-
<div class="geo-shape-3"></div>
|
|
175
|
-
|
|
176
|
-
<!-- Navigation -->
|
|
177
|
-
<div class="progress-bar" id="progressBar"></div>
|
|
178
|
-
<nav class="nav-dots" id="navDots" aria-label="Slide navigation"></nav>
|
|
179
|
-
|
|
180
|
-
<!-- Slides -->
|
|
181
|
-
<section class="slide title-slide" data-slide-type="cover" data-index="0">
|
|
182
|
-
<div class="slide-canvas"> ... </div>
|
|
183
|
-
</section>
|
|
184
|
-
<section class="slide" data-slide-type="content" data-index="1">
|
|
185
|
-
<div class="slide-canvas"> ... </div>
|
|
186
|
-
</section>
|
|
187
|
-
<!-- every <section class="slide"> must have data-slide-type — see SKILL.md for valid values -->
|
|
188
|
-
|
|
189
|
-
<script>/* all JS here */</script>
|
|
190
|
-
</body>
|
|
191
|
-
</html>
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Core CSS
|
|
195
|
-
|
|
196
|
-
```css
|
|
197
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
198
|
-
|
|
199
|
-
html {
|
|
200
|
-
scroll-snap-type: y mandatory;
|
|
201
|
-
overflow-y: scroll;
|
|
202
|
-
height: 100%;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
body {
|
|
206
|
-
background: var(--bg-primary);
|
|
207
|
-
color: var(--text-primary);
|
|
208
|
-
font-family: 'DM Sans', ui-sans-serif, sans-serif;
|
|
209
|
-
-webkit-font-smoothing: antialiased;
|
|
210
|
-
height: 100%;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.slide {
|
|
214
|
-
height: 100dvh;
|
|
215
|
-
scroll-snap-align: start;
|
|
216
|
-
overflow: hidden;
|
|
217
|
-
display: flex;
|
|
218
|
-
align-items: center;
|
|
219
|
-
justify-content: center;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.slide-canvas {
|
|
223
|
-
width: 1920px;
|
|
224
|
-
height: 1080px;
|
|
225
|
-
flex-shrink: 0;
|
|
226
|
-
transform-origin: center center;
|
|
227
|
-
/* scale set by JS setupScaling() */
|
|
228
|
-
overflow: hidden;
|
|
229
|
-
position: relative;
|
|
230
|
-
display: flex;
|
|
231
|
-
flex-direction: column;
|
|
232
|
-
justify-content: center;
|
|
233
|
-
padding: 60px 80px;
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Navigation & Progress
|
|
238
|
-
|
|
239
|
-
#### Progress Bar
|
|
240
|
-
|
|
241
|
-
```css
|
|
242
|
-
.progress-bar {
|
|
243
|
-
position: fixed;
|
|
244
|
-
top: 0;
|
|
245
|
-
left: 0;
|
|
246
|
-
height: 2px;
|
|
247
|
-
background: var(--border-strong);
|
|
248
|
-
width: 0%;
|
|
249
|
-
z-index: 100;
|
|
250
|
-
transition: width 0.2s ease;
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
No glow, no gradient — a quiet, thin line.
|
|
255
|
-
|
|
256
|
-
#### Nav Dots
|
|
257
|
-
|
|
258
|
-
```css
|
|
259
|
-
.nav-dots {
|
|
260
|
-
position: fixed;
|
|
261
|
-
right: 20px;
|
|
262
|
-
top: 50%;
|
|
263
|
-
transform: translateY(-50%);
|
|
264
|
-
display: flex;
|
|
265
|
-
flex-direction: column;
|
|
266
|
-
gap: 8px;
|
|
267
|
-
z-index: 100;
|
|
268
|
-
}
|
|
269
|
-
.nav-dots button {
|
|
270
|
-
width: 6px;
|
|
271
|
-
height: 6px;
|
|
272
|
-
border-radius: 50%;
|
|
273
|
-
border: 1px solid var(--border-strong);
|
|
274
|
-
background: transparent;
|
|
275
|
-
cursor: pointer;
|
|
276
|
-
padding: 0;
|
|
277
|
-
transition: background 0.2s;
|
|
278
|
-
}
|
|
279
|
-
.nav-dots button.active {
|
|
280
|
-
background: var(--text-primary);
|
|
281
|
-
border-color: var(--text-primary);
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
No glow, no box-shadow — solid fill only.
|
|
286
|
-
|
|
287
|
-
#### Reduced Motion
|
|
288
|
-
|
|
289
|
-
```css
|
|
290
|
-
@media (prefers-reduced-motion: reduce) {
|
|
291
|
-
.reveal {
|
|
292
|
-
transition: opacity 0.3s ease;
|
|
293
|
-
opacity: 1;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### SlidePresentation Class (Complete JavaScript)
|
|
299
|
-
|
|
300
|
-
All presentations must include this complete `SlidePresentation` class. Every
|
|
301
|
-
method is fully implemented — copy this exactly and include it in the `<script>`
|
|
302
|
-
block.
|
|
303
|
-
|
|
304
|
-
```javascript
|
|
305
|
-
class SlidePresentation {
|
|
306
|
-
constructor() {
|
|
307
|
-
this.slides = document.querySelectorAll('.slide');
|
|
308
|
-
this.currentSlide = 0;
|
|
309
|
-
this.setupScaling();
|
|
310
|
-
this.setupProgressBar();
|
|
311
|
-
this.setupNavDots();
|
|
312
|
-
this.setupIntersectionObserver();
|
|
313
|
-
this.setupKeyboardNav();
|
|
314
|
-
this.setupTouchNav();
|
|
315
|
-
this.setupMouseWheel();
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/* Scale 1920×1080 canvases to fit viewport */
|
|
319
|
-
setupScaling() {
|
|
320
|
-
const canvases = document.querySelectorAll('.slide-canvas');
|
|
321
|
-
const BASE_W = 1920, BASE_H = 1080;
|
|
322
|
-
const update = () => {
|
|
323
|
-
const vw = window.innerWidth;
|
|
324
|
-
const vh = window.innerHeight;
|
|
325
|
-
const scale = Math.min(vw / BASE_W, vh / BASE_H);
|
|
326
|
-
canvases.forEach(c => { c.style.transform = `scale(${scale})`; });
|
|
327
|
-
};
|
|
328
|
-
window.addEventListener('resize', update);
|
|
329
|
-
update();
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/* Horizontal progress bar at top */
|
|
333
|
-
setupProgressBar() {
|
|
334
|
-
const bar = document.getElementById('progressBar');
|
|
335
|
-
const update = () => {
|
|
336
|
-
const scrolled = window.scrollY;
|
|
337
|
-
const total = document.body.scrollHeight - window.innerHeight;
|
|
338
|
-
bar.style.width = total > 0 ? (scrolled / total * 100) + '%' : '0%';
|
|
339
|
-
};
|
|
340
|
-
window.addEventListener('scroll', update, { passive: true });
|
|
341
|
-
update();
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/* Vertical dot navigation on right side */
|
|
345
|
-
setupNavDots() {
|
|
346
|
-
const nav = document.getElementById('navDots');
|
|
347
|
-
this.slides.forEach((_, i) => {
|
|
348
|
-
const btn = document.createElement('button');
|
|
349
|
-
btn.setAttribute('aria-label', `Go to slide ${i + 1}`);
|
|
350
|
-
if (i === 0) btn.classList.add('active');
|
|
351
|
-
btn.addEventListener('click', () => this.goTo(i));
|
|
352
|
-
nav.appendChild(btn);
|
|
353
|
-
});
|
|
354
|
-
this.dots = nav.querySelectorAll('button');
|
|
355
|
-
|
|
356
|
-
const obs = new IntersectionObserver((entries) => {
|
|
357
|
-
entries.forEach(e => {
|
|
358
|
-
if (e.isIntersecting) {
|
|
359
|
-
const idx = parseInt(e.target.dataset.index);
|
|
360
|
-
this.dots.forEach((d, i) => d.classList.toggle('active', i === idx));
|
|
361
|
-
this.currentSlide = idx;
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}, { threshold: 0.5 });
|
|
365
|
-
this.slides.forEach(s => obs.observe(s));
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/* Reveal animations on scroll — opacity only, no translateY */
|
|
369
|
-
setupIntersectionObserver() {
|
|
370
|
-
const obs = new IntersectionObserver((entries) => {
|
|
371
|
-
entries.forEach(e => {
|
|
372
|
-
if (e.isIntersecting) {
|
|
373
|
-
e.target.querySelectorAll('.reveal').forEach(el => el.classList.add('visible'));
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
}, { threshold: 0.15 });
|
|
377
|
-
this.slides.forEach(s => obs.observe(s));
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/* Arrow keys, Space, PageUp/PageDown */
|
|
381
|
-
setupKeyboardNav() {
|
|
382
|
-
document.addEventListener('keydown', e => {
|
|
383
|
-
if (['ArrowDown', 'ArrowRight', ' ', 'PageDown'].includes(e.key)) {
|
|
384
|
-
e.preventDefault();
|
|
385
|
-
this.goTo(this.currentSlide + 1);
|
|
386
|
-
} else if (['ArrowUp', 'ArrowLeft', 'PageUp'].includes(e.key)) {
|
|
387
|
-
e.preventDefault();
|
|
388
|
-
this.goTo(this.currentSlide - 1);
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/* Swipe support for touch devices */
|
|
394
|
-
setupTouchNav() {
|
|
395
|
-
let startY = 0;
|
|
396
|
-
document.addEventListener('touchstart', e => { startY = e.touches[0].clientY; }, { passive: true });
|
|
397
|
-
document.addEventListener('touchend', e => {
|
|
398
|
-
const dy = startY - e.changedTouches[0].clientY;
|
|
399
|
-
if (Math.abs(dy) > 40) this.goTo(this.currentSlide + (dy > 0 ? 1 : -1));
|
|
400
|
-
}, { passive: true });
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
/* Debounced mouse wheel navigation (800ms) */
|
|
404
|
-
setupMouseWheel() {
|
|
405
|
-
let last = 0;
|
|
406
|
-
document.addEventListener('wheel', e => {
|
|
407
|
-
const now = Date.now();
|
|
408
|
-
if (now - last < 800) return;
|
|
409
|
-
last = now;
|
|
410
|
-
this.goTo(this.currentSlide + (e.deltaY > 0 ? 1 : -1));
|
|
411
|
-
}, { passive: true });
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/* Scroll to slide by index */
|
|
415
|
-
goTo(idx) {
|
|
416
|
-
const clamped = Math.max(0, Math.min(this.slides.length - 1, idx));
|
|
417
|
-
this.slides[clamped].scrollIntoView({ behavior: 'smooth' });
|
|
418
|
-
this.currentSlide = clamped;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
new SlidePresentation();
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
After the `SlidePresentation` class, include:
|
|
426
|
-
1. `lucide.createIcons();` to render Lucide icons (if icons are used)
|
|
427
|
-
2. ECharts initialization (if charts are present)
|
|
428
|
-
3. Inline editing code (see SKILL.md)
|
|
429
|
-
|
|
430
|
-
<!-- @section:global:end -->
|
|
431
|
-
|
|
432
|
-
<!-- @section:components:start -->
|
|
433
|
-
|
|
434
|
-
### Component Library
|
|
435
|
-
|
|
436
|
-
These are independent, composable building blocks. Mix them freely on any slide —
|
|
437
|
-
they are not locked to specific layouts.
|
|
438
|
-
|
|
439
|
-
<!-- @component:reveal:start -->
|
|
440
|
-
#### Reveal Animation (.reveal)
|
|
441
|
-
|
|
442
|
-
Add `.reveal` to any element that should animate on scroll. JS adds `.visible`
|
|
443
|
-
via IntersectionObserver. This theme uses opacity-only transitions — no
|
|
444
|
-
directional motion (translateY/translateX) — to preserve the calm, paper-like feel.
|
|
445
|
-
|
|
446
|
-
```css
|
|
447
|
-
.reveal {
|
|
448
|
-
opacity: 0;
|
|
449
|
-
transition: opacity 0.4s ease;
|
|
450
|
-
}
|
|
451
|
-
.reveal.visible {
|
|
452
|
-
opacity: 1;
|
|
453
|
-
}
|
|
454
|
-
/* Stagger children */
|
|
455
|
-
.reveal:nth-child(1) { transition-delay: 0s; }
|
|
456
|
-
.reveal:nth-child(2) { transition-delay: 0.06s; }
|
|
457
|
-
.reveal:nth-child(3) { transition-delay: 0.12s; }
|
|
458
|
-
.reveal:nth-child(4) { transition-delay: 0.18s; }
|
|
459
|
-
.reveal:nth-child(5) { transition-delay: 0.24s; }
|
|
460
|
-
.reveal:nth-child(6) { transition-delay: 0.30s; }
|
|
461
|
-
.reveal:nth-child(7) { transition-delay: 0.36s; }
|
|
462
|
-
.reveal:nth-child(8) { transition-delay: 0.42s; }
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
No `.title-reveal` in this theme — cover headings use the same `.reveal` fade.
|
|
466
|
-
|
|
467
|
-
<!-- @component:reveal:end -->
|
|
468
|
-
|
|
469
|
-
<!-- @component:showcase:start -->
|
|
470
|
-
#### Showcase (.showcase)
|
|
471
|
-
|
|
472
|
-
Semi-transparent container that frames a component or image with padding and
|
|
473
|
-
visual depth. Use inside `.two-col-aside`, `.two-col-main`, or as a standalone
|
|
474
|
-
wrapper when a component needs a "stage" rather than sitting directly on the
|
|
475
|
-
slide background. Vertically and horizontally centers its content.
|
|
476
|
-
|
|
477
|
-
```css
|
|
478
|
-
.showcase {
|
|
479
|
-
padding: 28px;
|
|
480
|
-
border: 1px solid var(--border);
|
|
481
|
-
background: var(--bg-card);
|
|
482
|
-
backdrop-filter: blur(16px);
|
|
483
|
-
-webkit-backdrop-filter: blur(16px);
|
|
484
|
-
border-radius: 12px;
|
|
485
|
-
display: flex;
|
|
486
|
-
align-items: center;
|
|
487
|
-
justify-content: center;
|
|
488
|
-
flex: 1;
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
<!-- @component:showcase:end -->
|
|
493
|
-
|
|
494
|
-
<!-- @component:card:start -->
|
|
495
|
-
#### Card (.card)
|
|
496
|
-
|
|
497
|
-
Frosted-glass container for any grouped content — features, evidence, info blocks.
|
|
498
|
-
|
|
499
|
-
```html
|
|
500
|
-
<div class="card">
|
|
501
|
-
<i data-lucide="circle-dashed" class="card-icon"></i>
|
|
502
|
-
<p class="card-label">01</p>
|
|
503
|
-
<p class="card-title">Title</p>
|
|
504
|
-
<p class="card-body">Body text describing the item.</p>
|
|
505
|
-
</div>
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
All inner elements are optional — use only what the content needs.
|
|
509
|
-
|
|
510
|
-
```css
|
|
511
|
-
.card {
|
|
512
|
-
background: var(--bg-card);
|
|
513
|
-
backdrop-filter: blur(16px);
|
|
514
|
-
-webkit-backdrop-filter: blur(16px);
|
|
515
|
-
border: 1px solid var(--border);
|
|
516
|
-
border-radius: 12px;
|
|
517
|
-
padding: 28px;
|
|
518
|
-
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
519
|
-
display: flex;
|
|
520
|
-
flex-direction: column;
|
|
521
|
-
justify-content: flex-start;
|
|
522
|
-
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
|
523
|
-
}
|
|
524
|
-
.card:hover {
|
|
525
|
-
transform: translateY(-2px);
|
|
526
|
-
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.10);
|
|
527
|
-
}
|
|
528
|
-
.card-icon { width: 28px; height: 28px; color: var(--accent-2); margin-bottom: 12px; transition: transform 0.2s ease; }
|
|
529
|
-
.card:hover .card-icon { transform: scale(1.05); }
|
|
530
|
-
.card-label { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 10px; }
|
|
531
|
-
.card-title { font-family: 'DM Serif Display', serif; font-size: 24px; margin-bottom: 12px; }
|
|
532
|
-
.card-body { font-size: 17px; color: var(--text-secondary); line-height: 1.7; flex: 1; }
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
<!-- @component:card:end -->
|
|
536
|
-
|
|
537
|
-
<!-- @component:image-card:start -->
|
|
538
|
-
#### Image Card (.image-card)
|
|
539
|
-
|
|
540
|
-
Standalone image with rounded corners and optional caption. Use for product shots,
|
|
541
|
-
screenshots, team photos, or any visual that deserves its own space.
|
|
542
|
-
|
|
543
|
-
```html
|
|
544
|
-
<div class="image-card">
|
|
545
|
-
<img src="photo.jpg" alt="Description">
|
|
546
|
-
<p class="image-card-caption">Optional caption text</p>
|
|
547
|
-
</div>
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
Caption is optional — omit when the image is self-explanatory.
|
|
551
|
-
|
|
552
|
-
```css
|
|
553
|
-
.image-card {
|
|
554
|
-
border-radius: 12px;
|
|
555
|
-
overflow: hidden;
|
|
556
|
-
border: 1px solid var(--border);
|
|
557
|
-
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
558
|
-
display: flex;
|
|
559
|
-
flex-direction: column;
|
|
560
|
-
}
|
|
561
|
-
.image-card img {
|
|
562
|
-
width: 100%;
|
|
563
|
-
height: 100%;
|
|
564
|
-
object-fit: cover;
|
|
565
|
-
display: block;
|
|
566
|
-
}
|
|
567
|
-
.image-card-caption {
|
|
568
|
-
padding: 14px 18px;
|
|
569
|
-
font-size: 14px;
|
|
570
|
-
color: var(--text-muted);
|
|
571
|
-
background: var(--bg-card);
|
|
572
|
-
backdrop-filter: blur(16px);
|
|
573
|
-
font-family: 'DM Sans', sans-serif;
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
Sizing: set `width` and `height` on `.image-card` via inline style to control
|
|
578
|
-
aspect ratio. Typical: `width:100%; height:400px` in a column, or
|
|
579
|
-
`width:480px; height:320px` standalone.
|
|
580
|
-
|
|
581
|
-
<!-- @component:image-card:end -->
|
|
582
|
-
|
|
583
|
-
<!-- @component:card-img:start -->
|
|
584
|
-
#### Card with Image Header (.card-img)
|
|
585
|
-
|
|
586
|
-
Card variant with an image at the top and text content below. Use for team
|
|
587
|
-
members, portfolio items, or any card where a visual header adds context.
|
|
588
|
-
|
|
589
|
-
```html
|
|
590
|
-
<div class="card card-img">
|
|
591
|
-
<div class="card-img-top">
|
|
592
|
-
<img src="photo.jpg" alt="Description">
|
|
593
|
-
</div>
|
|
594
|
-
<p class="card-title">Title</p>
|
|
595
|
-
<p class="card-body">Description text goes here.</p>
|
|
596
|
-
</div>
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
```css
|
|
600
|
-
.card-img {
|
|
601
|
-
padding: 0;
|
|
602
|
-
overflow: hidden;
|
|
603
|
-
}
|
|
604
|
-
.card-img-top {
|
|
605
|
-
width: 100%;
|
|
606
|
-
aspect-ratio: 16 / 10;
|
|
607
|
-
overflow: hidden;
|
|
608
|
-
}
|
|
609
|
-
.card-img-top img {
|
|
610
|
-
width: 100%;
|
|
611
|
-
height: 100%;
|
|
612
|
-
object-fit: cover;
|
|
613
|
-
display: block;
|
|
614
|
-
}
|
|
615
|
-
.card-img .card-title,
|
|
616
|
-
.card-img .card-body,
|
|
617
|
-
.card-img .card-label {
|
|
618
|
-
padding-left: 28px;
|
|
619
|
-
padding-right: 28px;
|
|
620
|
-
}
|
|
621
|
-
.card-img .card-title { padding-top: 20px; }
|
|
622
|
-
.card-img .card-body { padding-bottom: 28px; }
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
<!-- @component:card-img:end -->
|
|
626
|
-
|
|
627
|
-
<!-- @component:avatar:start -->
|
|
628
|
-
#### Avatar (.avatar)
|
|
629
|
-
|
|
630
|
-
Circular cropped image for people, team members, or profile pictures.
|
|
631
|
-
|
|
632
|
-
```html
|
|
633
|
-
<img src="person.jpg" alt="Name" class="avatar avatar-lg">
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
```css
|
|
637
|
-
.avatar {
|
|
638
|
-
width: 64px;
|
|
639
|
-
height: 64px;
|
|
640
|
-
border-radius: 50%;
|
|
641
|
-
object-fit: cover;
|
|
642
|
-
border: 2px solid var(--border-strong);
|
|
643
|
-
flex-shrink: 0;
|
|
644
|
-
}
|
|
645
|
-
.avatar-sm { width: 48px; height: 48px; }
|
|
646
|
-
.avatar-lg { width: 96px; height: 96px; }
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
No glow, no shadow — clean circle with a thin warm border. Pair with card text
|
|
650
|
-
for team slides: avatar left, name + role right.
|
|
651
|
-
|
|
652
|
-
<!-- @component:avatar:end -->
|
|
653
|
-
|
|
654
|
-
<!-- @component:stat-card:start -->
|
|
655
|
-
#### Stat Card (.stat-card)
|
|
656
|
-
|
|
657
|
-
Large metric display. Numbers appear statically with fade-in — no counter animation.
|
|
658
|
-
|
|
659
|
-
```html
|
|
660
|
-
<div class="stat-card">
|
|
661
|
-
<div class="stat-number">85%</div>
|
|
662
|
-
<div class="stat-label">Growth Rate</div>
|
|
663
|
-
<div class="stat-desc">Year over year revenue increase</div>
|
|
664
|
-
</div>
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
```css
|
|
668
|
-
.stat-card {
|
|
669
|
-
background: var(--bg-card);
|
|
670
|
-
backdrop-filter: blur(16px);
|
|
671
|
-
border: 1px solid var(--border);
|
|
672
|
-
border-radius: 12px;
|
|
673
|
-
padding: 48px 40px;
|
|
674
|
-
text-align: center;
|
|
675
|
-
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
676
|
-
border-bottom: 2px solid var(--border-strong);
|
|
677
|
-
display: flex;
|
|
678
|
-
flex-direction: column;
|
|
679
|
-
align-items: center;
|
|
680
|
-
justify-content: center;
|
|
681
|
-
}
|
|
682
|
-
.stat-number { font-family: 'DM Serif Display', serif; font-size: 64px; font-weight: 400; color: var(--text-primary); line-height: 1; letter-spacing: -0.03em; margin-bottom: 12px; }
|
|
683
|
-
.stat-label { font-size: 15px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 12px; }
|
|
684
|
-
.stat-desc { font-size: 15px; color: var(--text-secondary); line-height: 1.5; opacity: 0.8; }
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
No `.gradient-text` on numbers — minimal stays monochrome.
|
|
688
|
-
|
|
689
|
-
<!-- @component:stat-card:end -->
|
|
690
|
-
|
|
691
|
-
<!-- @component:quote-block:start -->
|
|
692
|
-
#### Quote Block (.quote-block)
|
|
693
|
-
|
|
694
|
-
```html
|
|
695
|
-
<div class="quote-deco">“</div>
|
|
696
|
-
<div class="quote-block">
|
|
697
|
-
<blockquote>“The quote text goes here.”</blockquote>
|
|
698
|
-
<cite>Attribution</cite>
|
|
699
|
-
</div>
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
```css
|
|
703
|
-
.quote-block { display: flex; flex-direction: column; justify-content: center; border-left: 2px solid var(--border-strong); padding-left: 32px; max-width: 1200px; }
|
|
704
|
-
.quote-block blockquote { font-family: 'DM Serif Display', serif; font-size: 40px; font-style: italic; line-height: 1.4; margin-bottom: 20px; }
|
|
705
|
-
.quote-block cite { font-size: 14px; color: var(--text-muted); letter-spacing: 0.05em; text-transform: uppercase; font-style: normal; font-family: 'DM Sans', sans-serif; }
|
|
706
|
-
.quote-deco { position: absolute; font-family: 'DM Serif Display', serif; font-size: 280px; line-height: 1; color: var(--border); opacity: 0.3; pointer-events: none; z-index: 0; }
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
No background color on `.quote-block` — let the typography speak.
|
|
710
|
-
|
|
711
|
-
<!-- @component:quote-block:end -->
|
|
712
|
-
|
|
713
|
-
<!-- @component:step-flow:start -->
|
|
714
|
-
#### Step Flow (.step-flow)
|
|
715
|
-
|
|
716
|
-
Horizontal process with numbered circles and connectors. Alternate `.step`
|
|
717
|
-
and `.step-connector` as siblings inside `.step-flow`.
|
|
718
|
-
|
|
719
|
-
```html
|
|
720
|
-
<div class="step-flow">
|
|
721
|
-
<div class="step">
|
|
722
|
-
<div class="step-circle">1</div>
|
|
723
|
-
<div class="step-title">Describe</div>
|
|
724
|
-
<div class="step-desc">Tell us your topic and audience.</div>
|
|
725
|
-
</div>
|
|
726
|
-
<div class="step-connector"></div>
|
|
727
|
-
<div class="step">
|
|
728
|
-
<div class="step-circle">2</div>
|
|
729
|
-
<div class="step-title">Generate</div>
|
|
730
|
-
<div class="step-desc">AI creates your slides.</div>
|
|
731
|
-
</div>
|
|
732
|
-
<!-- more steps + connectors as needed -->
|
|
733
|
-
</div>
|
|
734
|
-
```
|
|
735
|
-
|
|
736
|
-
```css
|
|
737
|
-
.step-flow { display: flex; align-items: flex-start; max-width: 1600px; margin: 0 auto; }
|
|
738
|
-
.step { flex: 1; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 16px; }
|
|
739
|
-
.step-circle { width: 64px; height: 64px; border-radius: 50%; background: var(--text-primary); color: var(--bg-primary); font-family: 'DM Serif Display', serif; font-size: 24px; font-weight: 400; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
740
|
-
.step-connector { flex: 0 0 32px; height: 1px; background: var(--border); margin-top: 32px; align-self: flex-start; }
|
|
741
|
-
.step-title { font-family: 'DM Serif Display', serif; font-size: 20px; }
|
|
742
|
-
.step-desc { font-size: 16px; color: var(--text-secondary); line-height: 1.6; max-width: 200px; }
|
|
743
|
-
```
|
|
744
|
-
|
|
745
|
-
Circles are solid fill — no gradient, no glow, no shadow.
|
|
746
|
-
|
|
747
|
-
<!-- @component:step-flow:end -->
|
|
748
|
-
|
|
749
|
-
<!-- @component:evidence-list:start -->
|
|
750
|
-
#### Evidence List (.evidence-list)
|
|
751
|
-
|
|
752
|
-
Styled bullet list with em-dash markers. Use inside `.card`, `.two-col-main`, or any container.
|
|
753
|
-
|
|
754
|
-
```html
|
|
755
|
-
<ul class="evidence-list">
|
|
756
|
-
<li>First piece of evidence or supporting point</li>
|
|
757
|
-
<li>Second supporting point</li>
|
|
758
|
-
</ul>
|
|
759
|
-
```
|
|
760
|
-
|
|
761
|
-
```css
|
|
762
|
-
.evidence-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 12px; }
|
|
763
|
-
.evidence-list li { padding-left: 18px; position: relative; font-size: 18px; color: var(--text-primary); line-height: 1.5; }
|
|
764
|
-
.evidence-list li::before { content: '\2014'; position: absolute; left: 0; top: 0; color: var(--border-strong); font-family: 'DM Serif Display', serif; }
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
<!-- @component:evidence-list:end -->
|
|
768
|
-
|
|
769
|
-
<!-- @component:chart-container:start -->
|
|
770
|
-
#### Chart Container (.chart-container)
|
|
771
|
-
|
|
772
|
-
Wrapper for any ECharts visualization. Can appear on any slide, in any layout.
|
|
773
|
-
See §Data Visualization for chart type selection and theme-specific styling.
|
|
774
|
-
|
|
775
|
-
```html
|
|
776
|
-
<div class="chart-container" id="chart-{purpose}"
|
|
777
|
-
style="width:{w}px; height:{h}px"></div>
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
```css
|
|
781
|
-
.chart-container { position: relative; flex-shrink: 0; }
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
Common sizing by context:
|
|
785
|
-
- Inside a column (`.two-col-main`, `.two-col-aside`): `width:100%; height:320–400px`
|
|
786
|
-
- Full-width standalone: `max-width:1400px; height:500px; margin:0 auto`
|
|
787
|
-
- Inside a `.card` (sparkline/mini): `width:100%; height:140–200px`
|
|
788
|
-
- Below a stats row: `max-width:1200px; height:300px; margin:24px auto 0`
|
|
789
|
-
|
|
790
|
-
JS init pattern (inside `window.addEventListener('load', ...)`):
|
|
791
|
-
```javascript
|
|
792
|
-
if (typeof echarts !== 'undefined') {
|
|
793
|
-
const el = document.getElementById('chart-{purpose}');
|
|
794
|
-
if (el) {
|
|
795
|
-
const chart = echarts.init(el, null, { renderer: 'canvas' });
|
|
796
|
-
chart.setOption({ /* see §Data Visualization for theme options */ });
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
<!-- @component:chart-container:end -->
|
|
802
|
-
|
|
803
|
-
<!-- @component:text-helpers:start -->
|
|
804
|
-
#### Text Helpers
|
|
805
|
-
|
|
806
|
-
```css
|
|
807
|
-
h1 { font-family: 'DM Serif Display', serif; font-size: 64px; font-weight: 400; line-height: 1.15; letter-spacing: -0.02em; }
|
|
808
|
-
h2 { font-family: 'DM Serif Display', serif; font-size: 42px; font-weight: 400; line-height: 1.2; letter-spacing: -0.01em; }
|
|
809
|
-
.label { font-size: 13px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); font-weight: 400; }
|
|
810
|
-
.subtitle { font-size: 22px; color: var(--text-secondary); font-weight: 300; line-height: 1.6; }
|
|
811
|
-
.body-text { font-size: 18px; color: var(--text-secondary); line-height: 1.75; }
|
|
812
|
-
```
|
|
813
|
-
|
|
814
|
-
Use `<em>` (italic) on the single most important phrase in a heading — never bold,
|
|
815
|
-
never color, never gradient. Max one emphasized phrase per slide.
|
|
816
|
-
|
|
817
|
-
<!-- @component:text-helpers:end -->
|
|
818
|
-
|
|
819
|
-
<!-- @component:dividers:start -->
|
|
820
|
-
#### Dividers and Rules
|
|
821
|
-
|
|
822
|
-
```css
|
|
823
|
-
.divider { border: none; border-top: 1px solid var(--border); margin: 24px 0; }
|
|
824
|
-
.divider-wide { border: none; border-top: 1px solid var(--border); width: 300px; }
|
|
825
|
-
.heading-rule { border: none; border-bottom: 1px solid var(--border); padding-bottom: 16px; margin-bottom: 24px; }
|
|
826
|
-
```
|
|
827
|
-
|
|
828
|
-
- `.divider` (full-width, `1px`): structural — separate heading from content
|
|
829
|
-
- `.divider-wide` (`300px`): decorative anchor — bottom of sparse slides
|
|
830
|
-
- `.heading-rule` (border-bottom on heading): separate title from body on content slides
|
|
831
|
-
- Never use gradient lines, colored borders, or thick rules
|
|
832
|
-
|
|
833
|
-
<!-- @component:dividers:end -->
|
|
834
|
-
|
|
835
|
-
<!-- @component:icons:start -->
|
|
836
|
-
#### Icons (Lucide)
|
|
837
|
-
|
|
838
|
-
Load via CDN: `<script src="https://cdn.jsdelivr.net/npm/lucide@latest/dist/umd/lucide.js"></script>`
|
|
839
|
-
Use `<i data-lucide="icon-name" class="card-icon"></i>` and call `lucide.createIcons()` in JS.
|
|
840
|
-
|
|
841
|
-
- Icons are **optional** in this theme — typography and whitespace provide the hierarchy
|
|
842
|
-
- Icon color: `var(--accent-2)` (warm grey), size: `1.75rem`, hover: `scale(1.05)`
|
|
843
|
-
- When used, pick simple stroke-style icons: `circle-dashed` → restraint, `eye` → clarity,
|
|
844
|
-
`text` → typography, `book-open` → research, `target` → precision
|
|
845
|
-
- Cards may include icons, but omitting them often feels more "minimal"
|
|
846
|
-
- Stats, quotes, step flow: never add icons
|
|
847
|
-
|
|
848
|
-
<!-- @component:icons:end -->
|
|
849
|
-
|
|
850
|
-
<!-- @component:deco-fills:start -->
|
|
851
|
-
#### Decorative Fills (.deco-circle, .deco-rule)
|
|
852
|
-
|
|
853
|
-
Positioned-absolute background elements behind content.
|
|
854
|
-
|
|
855
|
-
```css
|
|
856
|
-
.deco-circle { position: absolute; border-radius: 50%; pointer-events: none; z-index: 0; }
|
|
857
|
-
.deco-rule { position: absolute; pointer-events: none; z-index: 0; }
|
|
858
|
-
|
|
859
|
-
/* Ensure content stacks above decorative fills */
|
|
860
|
-
.slide-canvas > *:not(.deco-circle):not(.deco-rule):not(.quote-deco) { position: relative; z-index: 1; }
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
Set size, color, opacity, and position via inline styles:
|
|
864
|
-
- Typical `.deco-circle`: `width:200–400px; height:same; background:rgba(255,179,71,0.04–0.06); filter:blur(30–40px)`
|
|
865
|
-
- Typical `.deco-rule`: `width:120–200px; height:1px; background:linear-gradient(90deg, transparent, var(--border-strong), transparent); opacity:0.3–0.5`
|
|
866
|
-
- Sparse slides (cover, quote, closing): 2–3 deco elements
|
|
867
|
-
- Dense slides (grids, multi-column): 0 deco elements — content is the fill
|
|
868
|
-
- Never exceed 3 per slide
|
|
869
|
-
|
|
870
|
-
<!-- @component:deco-fills:end -->
|
|
871
|
-
|
|
872
|
-
<!-- @section:components:end -->
|
|
873
|
-
|
|
874
|
-
<!-- @section:layouts:start -->
|
|
875
|
-
### Layout Primitives
|
|
876
|
-
|
|
877
|
-
Components can be placed in any of these layout arrangements.
|
|
878
|
-
Mix layouts and components freely — these are tools, not templates.
|
|
879
|
-
|
|
880
|
-
#### Centered Stack
|
|
881
|
-
|
|
882
|
-
`.slide-canvas` default behavior — `flex-direction: column; justify-content: center`.
|
|
883
|
-
Use for: cover, closing, quote-focused, single-statement slides.
|
|
884
|
-
|
|
885
|
-
#### Top-Aligned Stack
|
|
886
|
-
|
|
887
|
-
Override `.slide-canvas` with `style="justify-content: flex-start; padding-top: 80px"`.
|
|
888
|
-
Use for: content-heavy slides where vertical space matters.
|
|
889
|
-
|
|
890
|
-
#### Two-Column Grid (.two-col)
|
|
891
|
-
|
|
892
|
-
```html
|
|
893
|
-
<div class="two-col">
|
|
894
|
-
<div class="two-col-main"><!-- primary content --></div>
|
|
895
|
-
<div class="two-col-aside"><!-- secondary content --></div>
|
|
896
|
-
</div>
|
|
897
|
-
```
|
|
898
|
-
|
|
899
|
-
```css
|
|
900
|
-
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: stretch; max-width: 1600px; margin: 0 auto; flex: 1; }
|
|
901
|
-
.two-col-main { display: flex; flex-direction: column; justify-content: center; }
|
|
902
|
-
.two-col-main h2 { font-size: 36px; margin-bottom: 16px; }
|
|
903
|
-
.two-col-main p { font-size: 18px; color: var(--text-secondary); line-height: 1.75; }
|
|
904
|
-
.two-col-aside { display: flex; flex-direction: column; justify-content: center; }
|
|
905
|
-
.two-col-aside .card { flex: 1; }
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
Either side can contain any components — cards, charts, text, evidence lists, stat cards.
|
|
909
|
-
|
|
910
|
-
#### Three-Column Grid
|
|
911
|
-
|
|
912
|
-
```css
|
|
913
|
-
.card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; max-width: 1600px; margin: 0 auto; flex: 1; align-content: stretch; }
|
|
914
|
-
.stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; max-width: 1600px; margin: 0 auto; }
|
|
915
|
-
```
|
|
916
|
-
|
|
917
|
-
Use `.card-grid` for cards, `.stats-row` for stat cards, or create a custom grid.
|
|
918
|
-
For 2 items: `repeat(2, 1fr)`. For 4: `repeat(2, 1fr)` (2×2 grid).
|
|
919
|
-
The grid container can hold any mix of components.
|
|
920
|
-
|
|
921
|
-
#### Horizontal Flow
|
|
922
|
-
|
|
923
|
-
Used by `.step-flow` — `display: flex; align-items: flex-start; max-width: 1600px`.
|
|
924
|
-
Also works for timelines, comparison strips, or any horizontal sequence.
|
|
925
|
-
|
|
926
|
-
<!-- @section:layouts:end -->
|
|
927
|
-
|
|
928
|
-
<!-- @section:charts:start -->
|
|
929
|
-
### Data Visualization (ECharts)
|
|
930
|
-
|
|
931
|
-
When a slide includes charts or data visualization, use [ECharts](https://echarts.apache.org/)
|
|
932
|
-
loaded via CDN:
|
|
933
|
-
|
|
934
|
-
```html
|
|
935
|
-
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
#### Chart Type Selection
|
|
939
|
-
|
|
940
|
-
Choose the chart type based on the data, not by habit. Different data patterns
|
|
941
|
-
demand different visualizations:
|
|
942
|
-
|
|
943
|
-
| Data Pattern | Chart Type | Common Placements |
|
|
944
|
-
|---|---|---|
|
|
945
|
-
| Proportions / composition (parts of a whole) | Pie / Donut | Any layout — pair with narrative or standalone |
|
|
946
|
-
| Trends over time (growth, decline, cycles) | Line or Area | Full-width for detail, two-col for trend + commentary |
|
|
947
|
-
| Category comparisons (A vs B vs C) | Vertical Bar | Full-width or two-col |
|
|
948
|
-
| Rankings / sorted magnitudes | Horizontal Bar | Full-width (labels need room) |
|
|
949
|
-
| Distribution / histogram | Vertical Bar (binned) | Full-width |
|
|
950
|
-
| KPIs with sparkline context | Stats Row + small Line chart below | Custom composite |
|
|
951
|
-
|
|
952
|
-
Layout is not fixed — pick the layout that serves the narrative. A chart can
|
|
953
|
-
occupy one column of Two-Column, span full-width on its own slide, or sit
|
|
954
|
-
inside a Feature Card as a small sparkline. Let the data and story decide.
|
|
955
|
-
|
|
956
|
-
#### Shared monochrome chart styles
|
|
957
|
-
|
|
958
|
-
All chart types share these minimal theme defaults:
|
|
959
|
-
|
|
960
|
-
- **Color palette**: `['#1A1916', '#6B6560', '#C9C4B8']` (monochrome warm — ink, mid-grey, border).
|
|
961
|
-
For >3 series, extend with `#A09890` (muted) and `#D5D0C8` (light warm).
|
|
962
|
-
- **Background**: `'transparent'`
|
|
963
|
-
- **Axis labels / legend text**: `var(--text-muted)` (`#A09890`), `14px` DM Sans font
|
|
964
|
-
- **Grid lines**: `var(--border)` (`#C9C4B8`) at 30% opacity — barely visible
|
|
965
|
-
- **Animation**: `false` or very subtle fade-in only (consistent with minimal's stillness)
|
|
966
|
-
- **Tooltip**: light background (`var(--bg-card)`), `var(--text-primary)` text, thin `var(--border)` border
|
|
967
|
-
|
|
968
|
-
#### Pie / Donut
|
|
969
|
-
|
|
970
|
-
- Inner radius `55-65%` for donut; `0` for full pie
|
|
971
|
-
- `borderRadius: 6`, `borderColor: var(--bg-primary)`, `borderWidth: 3` for segment gaps
|
|
972
|
-
- Center label: large text in `var(--text-primary)`, DM Serif Display font
|
|
973
|
-
- Legend: bottom-aligned, `itemWidth: 12`, `itemGap: 20`
|
|
974
|
-
- Container: `280–360px` square is typical
|
|
975
|
-
|
|
976
|
-
#### Bar (vertical & horizontal)
|
|
977
|
-
|
|
978
|
-
- No rounded caps — flat bar ends for a cleaner, more structural look
|
|
979
|
-
- Bar width: `40-60%` of category gap — never touch adjacent bars
|
|
980
|
-
- Solid fills only — no gradient fills (gradients break the monochrome discipline)
|
|
981
|
-
- For ≤5 categories: one shade per bar from monochrome warm. For single-series: solid `var(--text-primary)`
|
|
982
|
-
- Axis line: `1px` `var(--border)`. Tick marks: hidden
|
|
983
|
-
|
|
984
|
-
#### Line / Area
|
|
985
|
-
|
|
986
|
-
- Line width: `1.5-2px`, smooth curves (`smooth: true`) — thinner than aurora to feel quieter
|
|
987
|
-
- No glow or shadow effects
|
|
988
|
-
- Area fill: solid color at 6-10% opacity — no gradients
|
|
989
|
-
- Data points: show on hover only (`symbol: 'none'`, `emphasis: { symbol: 'circle' }`)
|
|
990
|
-
- For multiple series: distinguish with solid vs dashed lines, all within monochrome warm palette
|
|
991
|
-
|
|
992
|
-
#### Integration rules
|
|
993
|
-
|
|
994
|
-
- Initialize charts inside a `window.addEventListener('load', ...)` or after DOM ready
|
|
995
|
-
- Use `echarts.init(container, null, { renderer: 'canvas' })` — canvas renderer for performance
|
|
996
|
-
- Charts must use the monochrome warm palette — never default ECharts colors or colored accents
|
|
997
|
-
- Responsive: charts are inside the 1920×1080 canvas, scaled by JS `transform: scale()` —
|
|
998
|
-
no need for ECharts `resize()` handling
|
|
999
|
-
|
|
1000
|
-
<!-- @section:charts:end -->
|
|
1001
|
-
|
|
1002
|
-
<!-- @section:guide:start -->
|
|
1003
|
-
### Composition Guide
|
|
1004
|
-
|
|
1005
|
-
#### Common Recipes
|
|
1006
|
-
|
|
1007
|
-
These are starting points — not constraints. Combine any components with any
|
|
1008
|
-
layout primitive as the content demands.
|
|
1009
|
-
|
|
1010
|
-
| Content Pattern | Suggested Recipe | Minimal Notes |
|
|
1011
|
-
|---|---|---|
|
|
1012
|
-
| 3–4 parallel features | 3-col grid + card ×3 | Number labels (`01`, `02`). Icon optional. |
|
|
1013
|
-
| Key metrics (2–4 KPIs) | 3-col grid + stat-card ×3 | Static numbers, no counter animation. DM Serif Display. |
|
|
1014
|
-
| Narrative + evidence | two-col + serif heading in main, frosted card with evidence-list in aside | Em-dash bullets |
|
|
1015
|
-
| Sequential process (3–5 steps) | horizontal flow + step-flow | Filled dark circles, `1px` connectors. Quiet. |
|
|
1016
|
-
| Memorable quote | centered stack + quote-block + quote-deco | DM Serif Display italic, `border-left: 2px`. |
|
|
1017
|
-
| Single powerful statement | centered stack + large italic heading + deco fills | `<em>` on key phrase. No gradient-text. |
|
|
1018
|
-
| Data visualization | any layout + chart-container | Chart type per data — see §Data Visualization. Monochrome warm. |
|
|
1019
|
-
| KPIs with trend context | 3-col grid + stat-cards, then chart below | Static numbers + supporting chart |
|
|
1020
|
-
| Team / people (3–4) | 3-col grid + card ×3 (no icons) | card-title → name (serif), card-body → role |
|
|
1021
|
-
| Before vs After | two-col + content per side | Serif heading + em-dash evidence-list each column |
|
|
1022
|
-
| 6+ items on one topic | Split across 2 slides | Max 4 items per slide (stricter than other themes) |
|
|
1023
|
-
|
|
1024
|
-
#### Element Usage Rules
|
|
1025
|
-
|
|
1026
|
-
**Emphasis (`<em>`):** Use italic on the single most important phrase in a heading —
|
|
1027
|
-
never bold, never color, never gradient. Typical: core concept (`clear presentation`),
|
|
1028
|
-
key insight (`nothing left to take away`). Max one per slide.
|
|
1029
|
-
|
|
1030
|
-
**Dividers:** `.divider` (full-width) for structural separation. `.divider-wide` (`300px`)
|
|
1031
|
-
as decorative anchor at bottom of sparse slides. `.heading-rule` on content slide headings.
|
|
1032
|
-
Never gradient lines, colored borders, or thick rules.
|
|
1033
|
-
|
|
1034
|
-
**Icons (Lucide):** Optional. Typography and whitespace provide the hierarchy.
|
|
1035
|
-
When used: simple stroke-style in `var(--accent-2)`. Cards may include icons,
|
|
1036
|
-
but omitting often feels more "minimal." Stats, quotes, step flow: never add icons.
|
|
1037
|
-
|
|
1038
|
-
**Card labels:** Sequential numbers (`01`, `02`, `03`) preferred — orderly and typographic.
|
|
1039
|
-
Short category words acceptable when items are unordered. Omit when the title is clear.
|
|
1040
|
-
|
|
1041
|
-
**Decorative fills:** Sparse slides: 2–3 deco elements (warm-amber circles, thin rules).
|
|
1042
|
-
Dense slides: 0 — content is the fill. Never exceed 3 per slide.
|
|
1043
|
-
|
|
1044
|
-
#### Common Mistakes
|
|
1045
|
-
|
|
1046
|
-
- Using colored accents, gradients, or glows → stay monochrome warm.
|
|
1047
|
-
- Adding icons to every card when the heading is already clear → less is more.
|
|
1048
|
-
- Using counter animations on numbers → minimal uses static numbers with fade-in.
|
|
1049
|
-
- Using more than 4 items per slide → split across slides.
|
|
1050
|
-
- Adding decorative fills to dense content slides → content is the fill.
|
|
1051
|
-
- Always using donut charts for data → match chart type to data pattern (see §Data Visualization).
|
|
1052
|
-
- Leaving more than ~20% of canvas visually empty → expand content or add thin rules.
|
|
1053
|
-
|
|
1054
|
-
### Code Blocks (if any)
|
|
1055
|
-
|
|
1056
|
-
```css
|
|
1057
|
-
pre, code {
|
|
1058
|
-
background: #F0EEE8;
|
|
1059
|
-
border: 1px solid var(--border);
|
|
1060
|
-
border-radius: 6px;
|
|
1061
|
-
font-family: 'DM Mono', 'Söhne Mono', ui-monospace, monospace;
|
|
1062
|
-
font-size: 14px;
|
|
1063
|
-
color: var(--text-primary);
|
|
1064
|
-
}
|
|
1065
|
-
```
|
|
1066
|
-
|
|
1067
|
-
### Do & Don't
|
|
1068
|
-
|
|
1069
|
-
- **Do** use light backgrounds throughout — never dark
|
|
1070
|
-
- **Do** let the subtle geometric shapes add depth without overwhelming
|
|
1071
|
-
- **Do** use serif headings (`DM Serif Display`) contrasted with sans-serif body (`DM Sans`)
|
|
1072
|
-
- **Do** keep the accent monochrome — no colored accents
|
|
1073
|
-
- **Do** use frosted-glass cards with `backdrop-filter: blur(16px)` and `rgba(255,255,255,0.55)` background
|
|
1074
|
-
- **Do** use minimal animations — this theme values stillness
|
|
1075
|
-
- **Don't** use gradients, glows, or colored borders
|
|
1076
|
-
- **Don't** add too many geometric shapes — 2-3 is enough
|
|
1077
|
-
- **Don't** use more than 4 bullet points per slide
|
|
1078
|
-
|
|
1079
|
-
<!-- @section:guide:end -->
|