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