rikiki-deck 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +2 -0
  2. package/bin/lib/inline.mjs +231 -0
  3. package/bin/lib/starter.mjs +83 -0
  4. package/bin/rikiki.mjs +150 -0
  5. package/dist/deck-badge.js +1 -1
  6. package/dist/deck-callout.js +1 -1
  7. package/dist/deck-card.js +1 -1
  8. package/dist/deck-code.js +1 -1
  9. package/dist/deck-cover.js +2 -2
  10. package/dist/deck-feature-cards.js +1 -1
  11. package/dist/deck-feature.js +1 -1
  12. package/dist/deck-grid.js +1 -1
  13. package/dist/deck-kicker.js +1 -1
  14. package/dist/deck-md.js +1 -1
  15. package/dist/deck-mermaid.js +3 -3
  16. package/dist/deck-metric.js +1 -1
  17. package/dist/deck-notes.js +1 -1
  18. package/dist/deck-overview.js +7 -3
  19. package/dist/deck-photo.js +1 -1
  20. package/dist/deck-presenter.js +34 -7
  21. package/dist/deck-punch.js +1 -1
  22. package/dist/deck-root.js +11 -6
  23. package/dist/deck-section.js +2 -2
  24. package/dist/deck-shortcut.js +1 -1
  25. package/dist/deck-split.js +1 -1
  26. package/dist/deck-stack.js +1 -1
  27. package/dist/deck-stat.js +2 -2
  28. package/dist/deck-step-list.js +1 -1
  29. package/dist/deck-takeaway.js +2 -2
  30. package/dist/deck-tier-list.js +1 -1
  31. package/dist/index.js +28 -23
  32. package/dist/plugins/shiki.d.ts +0 -2
  33. package/dist/runtime/deck-root.d.ts +23 -0
  34. package/dist/shared-styles.js +1 -1
  35. package/dist/shiki.js +1 -1
  36. package/dist/standalone.js +139 -58
  37. package/dist/vendor/lit.js +3 -0
  38. package/dist/vendor/marked.js +46 -0
  39. package/dist/vendor/mermaid.min.js +2024 -0
  40. package/dist/vendor/shiki.js +57 -0
  41. package/docs/llms/rikiki-reference.md +718 -0
  42. package/llms.txt +50 -0
  43. package/package.json +21 -7
  44. package/themes/rikiki.css +10 -4
  45. package/themes/siliceum.css +10 -4
@@ -0,0 +1,718 @@
1
+ # Rikiki · LLM reference
2
+
3
+ This reference documents rikiki v0.4.0.
4
+
5
+ Exhaustive, self-consistent reference for authoring valid **rikiki** decks. Every
6
+ tag, attribute, slot, and token below was derived from the source in this repo
7
+ (`src/index.ts` is the canonical component list; `themes/rikiki.css` is the
8
+ canonical token list). Do not invent tags, attributes, or tokens · use only what
9
+ is listed here.
10
+
11
+ ---
12
+
13
+ ## 1 · What rikiki is
14
+
15
+ Rikiki is a presentation framework built as **Lit Web Components**. A deck is
16
+ plain HTML: there is **no build step** to author or run one. You load a theme
17
+ stylesheet, then the component bundle, then write a `<deck-root>` that wraps
18
+ `deck-*` slide elements.
19
+
20
+ Load order matters · **theme CSS first, then `dist/index.js`**:
21
+
22
+ ```html
23
+ <link rel="stylesheet" href="./tokens.css">
24
+ <script type="module" src="./dist/index.js"></script>
25
+ ```
26
+
27
+ `tokens.css` simply `@import`s the default theme (`themes/rikiki.css`); you may
28
+ link a theme directly instead (e.g. `themes/siliceum.css`).
29
+
30
+ Each direct child of `<deck-root>` is one slide. `<deck-root>` handles
31
+ navigation, hash routing, the progress bar, step dots, and the keyboard hint.
32
+
33
+ ---
34
+
35
+ ## 2 · Minimal deck
36
+
37
+ The canonical skeleton (see `starter.html`):
38
+
39
+ ```html
40
+ <!doctype html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="UTF-8">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
+ <title>My deck</title>
46
+ <link rel="stylesheet" href="./tokens.css">
47
+ <script type="module" src="./dist/index.js"></script>
48
+ </head>
49
+ <body>
50
+ <deck-root>
51
+ <deck-cover brand="my talk" speaker="Your name" duration="~25 min" audience="Your audience">
52
+ <h1>Deck <span class="accent">title</span></h1>
53
+ <p class="sub">Subtitle or opening question</p>
54
+ </deck-cover>
55
+
56
+ <deck-section num="Section 1">
57
+ <h1>A first<br>chapter</h1>
58
+ </deck-section>
59
+
60
+ <deck-feature eyebrow="Topic">
61
+ <h1 slot="title">Headline · sharp claim</h1>
62
+ <p slot="lead" class="lead">One sentence that frames the slide.</p>
63
+ <deck-code lang="js" hero>
64
+ const greet = (name) => `Hello, ${name}!`;
65
+ </deck-code>
66
+ </deck-feature>
67
+ </deck-root>
68
+ </body>
69
+ </html>
70
+ ```
71
+
72
+ (Adjust the relative paths to wherever rikiki's `tokens.css` and `dist/` sit
73
+ relative to your deck file.)
74
+
75
+ ---
76
+
77
+ ## 3 · Navigation & hash
78
+
79
+ `<deck-root>` listens for keys globally (ignored while typing in an
80
+ `input`/`textarea`/`[contenteditable]`).
81
+
82
+ | Key(s) | Action |
83
+ |--------|--------|
84
+ | `→` / `↓` | Advance (next step, else next slide) |
85
+ | `←` / `↑` | Back |
86
+ | `Space` / `PageDown` | Advance |
87
+ | `PageUp` | Back |
88
+ | `Home` | First slide |
89
+ | `End` | Last slide |
90
+ | `O` | Overview grid (toggle; `Esc`/`Enter`/`O` exit) |
91
+ | `P` | Presenter / speaker window (toggle) |
92
+ | `?` / `H` | Keyboard help overlay |
93
+ | `Esc` | Close help overlay |
94
+ | `B` / `.` | Blank screen black (any key restores) |
95
+ | `W` / `,` | Blank screen white (any key restores) |
96
+
97
+ **2D navigation** is **opt-in** via `nav="2d"` on `<deck-root>` (it also needs
98
+ 2+ chapters, bounded by `<deck-section>` markers, with at least one multi-slide
99
+ chapter). In 2D mode: `←`/`→` move between chapters, `↑`/`↓` move within a
100
+ chapter (both fall back to linear motion at the edges). Without `nav="2d"` the
101
+ deck stays **linear**: arrows always move to the next/previous slide, so adding
102
+ sections never silently remaps `←`/`→` to chapter jumps.
103
+
104
+ **Mouse navigation** is on by default (since 0.3.0): left click advances
105
+ (Shift+click goes back), the scroll wheel navigates with a trackpad-friendly
106
+ debounce, discreet chevrons sit bottom-right (2D-aware), and mouse
107
+ back/forward buttons map to back/advance. The wheel yields to scrollable
108
+ content: a wheel over an overflowing descendant (a tall `deck-code` block, a
109
+ zoomable inline `<svg>`, …) scrolls it natively and only advances the deck once
110
+ that element reaches its scroll edge. The bottom-left key-hint chips
111
+ (`← → O P ?`) are also clickable shortcuts for the matching action. Interactive
112
+ elements (`a`, `button`, inputs, `[contenteditable]`) never trigger navigation;
113
+ add `data-no-advance` to opt any element out. Configure with the `mouse-nav`
114
+ attribute on `<deck-root>`:
115
+
116
+ | Value | Meaning |
117
+ |-------|---------|
118
+ | *(absent)* | Everything on · the default |
119
+ | `mouse-nav="none"` | Keyboard-only deck (pre-0.3 behavior) |
120
+ | `mouse-nav="wheel arrows"` | Granular subset of `click`, `wheel`, `arrows`, `aux` |
121
+
122
+ Chevron styling tokens: `--deck-root-nav-color`, `--deck-root-nav-bg`,
123
+ `--deck-root-nav-opacity`.
124
+
125
+ **`<deck-root>` attributes** (all optional):
126
+
127
+ | Attribute | Values | Effect |
128
+ |-----------|--------|--------|
129
+ | `nav` | `2d` | Opt into 2D (chapter/slide) navigation · see above |
130
+ | `mouse-nav` | *(absent)* / `none` / subset of `click wheel arrows aux` | Mouse navigation config · see above |
131
+ | `transition` | `slide` (default-ish) / `slide-up` / `slide-down` / `slide-right` / `fade` / `zoom` / `flip` | Deck-wide slide transition (see table below) |
132
+ | `autoplay` | integer ms (e.g. `8000`) | Auto-advance every N ms; pauses on hover, resets on any manual nav. `0`/absent = off |
133
+ | `loop` | *(boolean)* | With `autoplay`, wraps from the last slide back to the first |
134
+ | `swipe` | *(boolean)* | Pointer-driven horizontal swipe (touch + mouse): a swipe ≥ 60 px advances / goes back |
135
+
136
+ **Transition values** (deck-wide via `transition="…"` on `<deck-root>`, or
137
+ per-slide via `data-transition="…"` on a slide host). When unset, the effective
138
+ default is `fade`:
139
+
140
+ | Value | Effect |
141
+ |-------|--------|
142
+ | `slide` | Horizontal slide, direction-aware (back navigation slides the other way) |
143
+ | `slide-up` | Vertical slide from the bottom |
144
+ | `slide-down` | Vertical slide from the top |
145
+ | `slide-right` | Horizontal slide from the left |
146
+ | `fade` | Cross-fade with a slight scale (the effective default) |
147
+ | `zoom` | Scale-in / scale-out |
148
+ | `flip` | 3D flip on the Y axis |
149
+
150
+ A per-slide `data-transition` overrides the deck-wide `transition` for that one
151
+ navigation. (A `data-morph` reveal temporarily suppresses the transition so the
152
+ two don't fight · see §7.)
153
+
154
+ **Hash format** (deep-linking):
155
+
156
+ | Form | Meaning |
157
+ |------|---------|
158
+ | `#3` | slide 3 (1-based) |
159
+ | `#3.2` | slide 3, step 2 |
160
+ | `#2.3` | chapter 2, slide 3 (2D nav only) |
161
+ | `#2.3s1` | chapter 2, slide 3, step 1 (2D nav only) |
162
+
163
+ The 2D forms only apply when 2D navigation is active; otherwise `#a.b` is read as
164
+ `slide.step`.
165
+
166
+ Out-of-range deep links are **clamped to the nearest valid position**, not reset
167
+ to the first slide: a slide/chapter index past the end lands on the last one, and
168
+ a step past a slide's range settles on its last step. This keeps a bookmarked
169
+ `#4.3` usable while you iterate (e.g. after deleting a bullet that had a click).
170
+
171
+ ---
172
+
173
+ ## 4 · Layouts (slide-level containers)
174
+
175
+ Direct children of `<deck-root>`. Each is one slide.
176
+
177
+ | Tag | Purpose | Key attributes | Slots |
178
+ |-----|---------|----------------|-------|
179
+ | `deck-cover` | Opening slide, dark, with brand + meta | `brand` (split on " · "), `brand-src` (logo URL), `speaker`, `company`, `duration`, `audience`, `runtime`; per-row label overrides `speaker-label`, `company-label`, `duration-label`, `audience-label`, `runtime-label` | default `<h1>`, `.sub`/`p[slot=sub]` |
180
+ | `deck-section` | Chapter divider (also a chapter boundary for 2D nav) | `num` | default `<h1>` (may use `<em>`) |
181
+ | `deck-feature` | Headline + lead + one focal block | `eyebrow` | `title` (`<h1>`), `lead`, default (focal block, e.g. `deck-code`) |
182
+ | `deck-split` | Two or three columns side by side | `eyebrow`, `cols` (`1-1`/`1-2`/`2-1`/`3`), `gap` (1..6 or raw CSS length), `col-gap` (1..6 or raw CSS length) | `title`, `lead`; `left`/`right` (2-col) or `a`/`b`/`c` (3-col) |
183
+ | `deck-feature-cards` | Hero focal block + two detail cards under it | `eyebrow` | `title`, `lead`, default (hero block), `left`, `right` |
184
+ | `deck-photo` | Full-bleed image slide, content on overlay | `src` (required), `position` (CSS `object-position`, default `center`), `darken` (0..1 overlay alpha, default `0.35`), `align` (top/center/bottom, default center), `text-align` (left/center/right, default left) | default slot: any content; style slotted children with `.sub`/`.kicker` classes (these are **CSS classes**, not named slots) |
185
+ | `deck-takeaway` | Centered punchline, dark | `kicker` | default (e.g. `p.display`, `p.caption`, a `deck-callout`) |
186
+
187
+ ---
188
+
189
+ ## 5 · Molecules (composed containers)
190
+
191
+ | Tag | Purpose | Key attributes | Slots / children |
192
+ |-----|---------|----------------|------------------|
193
+ | `deck-callout` | Highlighted note box | `type` (`info`/`warn`/`danger`/`ok`) | default (text / `deck-md`) |
194
+ | `deck-card` | Tinted card | `color` (`yellow`/`orange`/`green`/`red`), `center`, `compact` | default (`<h3>` + body) |
195
+ | `deck-md` | Render Markdown (GFM) | · | default = raw Markdown text |
196
+ | `deck-mermaid` | Render a Mermaid diagram (loads Mermaid from CDN) | `compact` | default = Mermaid source |
197
+ | `deck-stat` | Big-number visual | `num`, `tone` (`yellow`/`orange`/`green`/`red`/`purple`/`lime`/`cyan`) | `claim` (`<h3>`), default = body line |
198
+ | `deck-metric-list` | Wraps `deck-metric` rows | · | `deck-metric` children |
199
+ | `deck-metric` | One metric row | `severity` (`bad`/`warn`/`ok`/`info`), `value`, `mono` (render the value in the mono font) | default = label |
200
+ | `deck-tier-list` | Tier ladder | · | `deck-tier`, `deck-tier-arrow` children |
201
+ | `deck-tier` | One tier row | `name`, `speed`, `severity` (`muted`/`warn`/`ok`/`hot`), `hot` | default = description text |
202
+ | `deck-tier-arrow` | Separator note between tiers | · | default = text |
203
+ | `deck-step-list` | Numbered step ladder | · | `deck-step` children |
204
+ | `deck-step` | One step row | `n`, `note` | default = label |
205
+ | `deck-shortcut-list` | Shortcut grid | `cols` (column count, e.g. `1`), `col-gap` (1..6) | `deck-shortcut` children |
206
+ | `deck-shortcut` | One keyboard-shortcut row | `keys` (space-separated), `label`, `note`, `tone` (`accent`/`ok`) | default = note |
207
+ | `deck-kbd` | Inline key chip | `tone` (`accent`/`ok`) | default = key text |
208
+ | `deck-stack` | Flex stack helper | `gap` (1..6), `direction` (`row`/`column`), `align` (`start`/`center`/`end`/`stretch`), `justify` (`start`/`center`/`end`/`between`/`around`), `fill` (grow to fill the cross axis) | children |
209
+ | `deck-grid` | CSS grid helper | `cols` (1..12 or template), `rows`, `gap` (1..6 or CSS), `align`, `justify`, `fill` | children |
210
+
211
+ ---
212
+
213
+ ## 6 · Atoms (primitives)
214
+
215
+ | Tag | Purpose | Key attributes | Slots |
216
+ |-----|---------|----------------|-------|
217
+ | `deck-badge` | Small status badge | `type` (`bad`/`ok`/`info`/`warn`/`neutral`) | default = text |
218
+ | `deck-kicker` | Uppercase eyebrow label | `on-dark` | default = text |
219
+ | `deck-punch` | Short punchy line | `tone` (`warn`/`danger`/`ok`/`info`/`muted`/`accent`; inherits text color if absent), `size` (`lead`/`big`/`mega`/`stat`/`display`), `weight` (`700`/`800`/`900`), `align` (`left`/`center`/`right`) | default = text |
220
+ | `deck-code` | Syntax-highlighted code | `lang`, `hero`, `nested`, `step-groups` | default = code text |
221
+
222
+ ### deck-code details
223
+
224
+ - `lang` · drives highlighting: `js` / `ts` / `json` (default), `html` / `xml` /
225
+ `svg`, `css` / `scss` / `less`.
226
+ - `hero` · centers the block vertically as the slide's focal element.
227
+ - `nested` · lighter border, no shadow (for use inside a `deck-card`).
228
+ - `step-groups` · a JSON array attribute that turns the snippet into a stepped
229
+ reveal; the number of groups becomes the slide's step count (see §7).
230
+
231
+ Highlighting is done client-side with a built-in regex highlighter (no build
232
+ step). An opt-in **Shiki plugin** can upgrade every `deck-code` block to
233
+ Shiki's grammars/themes.
234
+
235
+ ### Shiki plugin (optional, opt-in)
236
+
237
+ `src/plugins/shiki.ts` (`dist/shiki.js`) re-renders all `<deck-code>` blocks
238
+ through [Shiki](https://shiki.style), loaded from a CDN on first use. Install it
239
+ after the rikiki bundle:
240
+
241
+ ```html
242
+ <script type="module" src="./dist/index.js"></script>
243
+ <script type="module">
244
+ import { installShiki } from './dist/shiki.js';
245
+ await installShiki({ theme: 'one-dark-pro', langs: ['ts', 'tsx', 'html', 'css'] });
246
+ </script>
247
+ ```
248
+
249
+ API:
250
+
251
+ ```ts
252
+ async function installShiki(opts?: {
253
+ theme?: string; // any Shiki theme name (https://shiki.style/themes) · default 'one-dark-pro'
254
+ langs?: string[]; // grammars to preload · default ['ts', 'js', 'html', 'css', 'json']
255
+ cdn?: string; // CDN base to load Shiki from · default 'https://esm.sh/'
256
+ }): Promise<void>
257
+ ```
258
+
259
+ - Any Shiki theme/language works (not just the built-in highlighter's set); set
260
+ `langs` to whatever your deck uses.
261
+ - Shiki's inline token colors are stripped so the deck's `--rik-code__syntax-*`
262
+ tokens still theme the output.
263
+ - A language not loaded falls back silently to the built-in regex highlighter
264
+ (no error).
265
+ - **Trade-off:** pulls ~300 KB of Shiki + requested grammars from the CDN. That
266
+ is why it is opt-in · the core bundle stays ~12 KB gzip. Use `cdn` to point at
267
+ a self-hosted mirror.
268
+
269
+ ---
270
+
271
+ ## 7 · Steps & animations
272
+
273
+ Two independent mechanisms drive in-slide reveals; both serialize through the
274
+ slide's `step` so they also work inside presenter mirror iframes.
275
+
276
+ ### Built-in steps (no plugin)
277
+
278
+ - **`steps="N"`** (or `data-steps="N"`) on a slide host declares N reveal steps.
279
+ - **`[data-step-block]`** elements inside a slide are hidden until their step is
280
+ reached. `<deck-root>` toggles their visibility as `step` advances.
281
+ - **`deck-code[step-groups='[…]']`** · a code block whose `step-groups` JSON
282
+ array defines line groups revealed step by step; its group count sets the
283
+ slide's step count automatically.
284
+
285
+ The step dots at the bottom of the deck reflect the active slide's step count.
286
+
287
+ ### Click-stages plugin (per-element reveals)
288
+
289
+ `src/plugins/click-stages.ts` adds Slidev-style `v-click` reveals. rikiki drives
290
+ them with **attributes** (`data-click` on any element) · it does **not** support
291
+ Slidev's `<v-click>` / `<v-clicks>` wrapper elements. It is **opt-in** · not part
292
+ of the core bundle. Install it after rikiki loads:
293
+
294
+ ```html
295
+ <script type="module" src="./dist/index.js"></script>
296
+ <script type="module">
297
+ import { installClickStages } from './dist/click-stages.js';
298
+ installClickStages();
299
+ </script>
300
+ ```
301
+
302
+ Then annotate any element inside a slide:
303
+
304
+ | Attribute | Effect |
305
+ |-----------|--------|
306
+ | `data-click` | Hidden initially; appears at the next click (bare attributes get sequential steps in document order) |
307
+ | `data-click="N"` | Appears at explicit step N |
308
+ | `data-click-hide` | Visible initially; hidden once its click step is reached (`data-click-hide="N"` for an explicit step) |
309
+ | `data-anim="…"` | Reveal animation: `fade` (default), `slide-up`, `slide-down`, `slide-left`, `slide-right`, `scale`, `blur`, `flip-up`, `draw` (traces stroked SVG paths) |
310
+ | `data-anim-duration="600"` | Per-element duration in ms (default 320) |
311
+ | `data-anim-delay="120"` | Per-element delay in ms (default 0) |
312
+ | `data-anim-ease="…"` | `out` (default), `spring`, `in-out`, or any raw `cubic-bezier(…)` |
313
+ | `data-click-auto="800"` | **No click consumed** · reveals 800 ms after the previous stage (or slide activation). Consecutive autos chain · one click can drive a whole choreography |
314
+ | `data-click-stagger="80"` | On a container · **one** click flips its children in a cascade, 80 ms apart (`"0"` = simultaneous). A child with `data-click-hide` hides at that step instead of revealing |
315
+ | `data-click-children` | On a container · each direct child becomes its own sequential click, inheriting the container's `data-anim*` |
316
+ | `data-morph="key"` | Pair two elements (across steps of one slide, or across consecutive slides) · the element glides/resizes from A to B like Keynote's Magic Move. Uses the View Transitions API, with a WAAPI FLIP fallback on browsers without it (Firefox). Targets must be light-DOM elements |
317
+
318
+ Example:
319
+
320
+ ```html
321
+ <deck-feature eyebrow="Demo">
322
+ <h1 slot="title">Click stages</h1>
323
+ <p data-click data-anim="slide-up" data-anim-duration="600" data-anim-ease="spring">First reveal</p>
324
+ <p data-click-auto="500">Follows the first reveal automatically after 500 ms</p>
325
+ <ul data-click-stagger="80" data-anim="slide-up">
326
+ <li>wave 1</li><li>wave 2</li><li>wave 3</li>
327
+ </ul>
328
+ <p data-click="3" data-anim="scale">Explicit step</p>
329
+ </deck-feature>
330
+ ```
331
+
332
+ Morph pairs that swap on the same click need explicit steps
333
+ (`data-click-hide="1"` on the outgoing element, `data-click="1"` on the
334
+ incoming one) · bare attributes would put them on two sequential clicks.
335
+
336
+ The plugin patches `deck-root` so its step counter accounts for `[data-click]`
337
+ elements, and stepping toggles their visibility. Going back cancels pending
338
+ auto/stagger timers. Deep links and back-navigation settle instantly (no
339
+ replayed delays). It respects `prefers-reduced-motion`. When a `data-morph`
340
+ navigation runs, the deck-wide `transition="…"` animation is skipped for that
341
+ navigation so the two don't fight, and pending auto/stagger reveals start
342
+ once the morph settles instead of firing mid-transition. On auto/stagger
343
+ elements, `data-anim-delay` is folded into the timer (delays add up once,
344
+ they don't apply twice).
345
+
346
+ ---
347
+
348
+ ## 8 · Presenter mode
349
+
350
+ Press **`P`** to open a speaker window. It mirrors the current slide and the
351
+ next slide (rendered live via the rikiki bundle), shows a timer/clock, and
352
+ displays the speaker notes for the current slide.
353
+
354
+ Speaker notes live in a `<deck-notes>` element placed inside any slide host.
355
+ It is hidden in the deck itself; only the presenter window reads its text.
356
+
357
+ ```html
358
+ <deck-feature>
359
+ <h1 slot="title">My slide</h1>
360
+ <deck-notes>
361
+ - Mention the migration story
362
+ - Pause for laughter on the Java joke
363
+ </deck-notes>
364
+ </deck-feature>
365
+ ```
366
+
367
+ ---
368
+
369
+ ## 9 · Multi-deck assembly
370
+
371
+ Split a long talk into small partial files and assemble them into one deck at
372
+ build time. The assembler is `build/vite-deck.mjs` (pure Node · no runtime
373
+ weight added).
374
+
375
+ A `deck.config.js` (or `.json`) describes the deck:
376
+
377
+ ```js
378
+ export default {
379
+ title: 'My talk',
380
+ theme: '../../tokens.css', // theme href, relative to the OUTPUT file
381
+ bundle: '../../dist/index.js', // rikiki bundle href, relative to OUTPUT
382
+ transition: 'slide', // optional <deck-root transition="…">
383
+ slides: [
384
+ 'parts/cover.html',
385
+ 'parts/intro.md',
386
+ 'parts/closing.html',
387
+ ],
388
+ };
389
+ ```
390
+
391
+ - **`.html` partials** are inlined verbatim (one or more `deck-*` elements each).
392
+ - **`.md` partials** can hold one or many slides. A line that is exactly `---`
393
+ splits the file into separate slides (reveal.js convention); each chunk is
394
+ wrapped into its own `<deck-feature><deck-md>…</deck-md></deck-feature>`. Use
395
+ `***` for a horizontal rule inside a slide (since `---` is the slide break).
396
+
397
+ Run it:
398
+
399
+ ```bash
400
+ node build/vite-deck.mjs decks/example/deck.config.js
401
+ # or with an explicit output path:
402
+ node build/vite-deck.mjs decks/example/deck.config.js dist-decks/example.html
403
+ ```
404
+
405
+ There are also npm scripts: `npm run deck <config>` and `npm run deck:example`.
406
+ The default output file is named from `title` and written next to the config.
407
+
408
+ ### Bundling caveat for assembled decks
409
+
410
+ > The single-file export step (`bundle.mjs`, §12) only rewrites paths that use
411
+ > the `rikiki/…` convention · specifically references matching
412
+ > `rikiki/(dist|themes|tokens.css)` (as the decks under `examples/` do). It does
413
+ > **not** resolve plain relative paths like `../../dist/index.js`.
414
+ >
415
+ > The in-repo `decks/example` deliberately uses `../../dist/index.js` /
416
+ > `../../tokens.css` relative paths so it can be **served directly** for dev. As
417
+ > a result, `decks/example`'s assembled output is meant for direct serving and
418
+ > does **not** bundle via `bundle.mjs` as-is. To produce a bundleable assembled
419
+ > deck, point its `deck.config.js` `theme`/`bundle` at the `rikiki/…`-style paths
420
+ > that `bundle.mjs` rewrites.
421
+
422
+ ---
423
+
424
+ ## 10 · Livereload (authoring only)
425
+
426
+ `src/livereload.ts` polls the `Last-Modified`/etag of the deck's files and
427
+ auto-reloads the page when any change (showing a brief toast and keeping the
428
+ current slide via the hash). It watches: the deck's `<link rel="stylesheet">`
429
+ hrefs, the rikiki component files in `dist/`, and the deck HTML itself.
430
+
431
+ Enable it two ways:
432
+
433
+ - **`?live`** on the deck URL · `dist/index.js` lazy-imports the poller only when
434
+ this query param is present, e.g. `…/starter.html?live`.
435
+ - **Load the module directly** · `<script type="module" src="./dist/livereload.js">`
436
+ (it auto-starts on import).
437
+
438
+ Livereload is for authoring only; never ship it in a presented or bundled deck.
439
+
440
+ ---
441
+
442
+ ## 11 · Theming tokens
443
+
444
+ The theme defines three layers (`themes/rikiki.css`):
445
+
446
+ 1. **Palette** (`--rik-palette-*`) · raw colors, **private**; never consume
447
+ directly.
448
+ 2. **Semantic** (`--rik-<role>--<modifier>`) · the **public** API; this is what
449
+ decks and components reference.
450
+ 3. **Component** (`--deck-<tag>-*`) · per-component knobs that default to
451
+ semantic tokens; override on a host to retheme one instance.
452
+
453
+ Override semantic tokens at `:root` to retheme the whole deck, or set component
454
+ tokens on a single host. Representative semantic tokens (see `themes/rikiki.css`
455
+ for the full list):
456
+
457
+ | Group | Examples |
458
+ |-------|----------|
459
+ | Surfaces | `--rik-surface-page`, `--rik-surface-raised`, `--rik-surface-inverse`, `--rik-surface-inverse--soft` |
460
+ | Text | `--rik-text-default`, `--rik-text-default--muted`, `--rik-text-inverse`, `--rik-text-inverse--muted` |
461
+ | Borders | `--rik-border-default`, `--rik-border-inverse` |
462
+ | Accent | `--rik-accent`, `--rik-accent--soft`, `--rik-accent--strong`, `--rik-accent__on` |
463
+ | Status | `--rik-status-success`, `--rik-status-danger`, `--rik-status-warn`, `--rik-status-info` (each with `__bg` / `__border`) |
464
+ | Spacing | `--rik-space-1` … `--rik-space-6`, `--rik-space-hair`, `--rik-space-2xs` |
465
+ | Radius | `--rik-radius-xs`, `--rik-radius-sm`, `--rik-radius-md`, `--rik-radius-lg`, `--rik-radius-pill` |
466
+ | Fonts | `--rik-font-sans`, `--rik-font-display`, `--rik-font-mono` |
467
+ | Type scale | `--rik-text-xs` … `--rik-text-4xl`, plus legacy `--rik-font-size-*` aliases |
468
+ | Motion | `--rik-motion-fast`, `--rik-motion-base`, `--rik-motion-slow`, `--rik-motion__ease-out` |
469
+ | Code surface | `--rik-code__bg`, `--rik-code__text`, `--rik-code__syntax-keyword`, … |
470
+
471
+ **Deck chrome** · `<deck-root>` exposes component tokens to restyle its own
472
+ overlay UI (set them on `deck-root` or at `:root`):
473
+
474
+ | Token | Controls |
475
+ |-------|----------|
476
+ | `--deck-root-bg` | Deck background |
477
+ | `--deck-root-progress-color`, `--deck-root-progress-height` | Progress bar |
478
+ | `--deck-root-counter-color` | Slide counter text |
479
+ | `--deck-root-dot-bg`, `--deck-root-dot-active-bg` | Step dots (idle / active) |
480
+ | `--deck-root-kb-hint-color` | Bottom-left key-hint chips |
481
+ | `--deck-root-nav-color`, `--deck-root-nav-bg`, `--deck-root-nav-opacity` | Mouse-nav chevrons |
482
+
483
+ The theme zeroes motion durations under `prefers-reduced-motion: reduce`.
484
+
485
+ Light-DOM helper classes the theme ships (use on slotted children):
486
+ `.accent`, `.accent-danger`, `.accent-warn`, `.accent-success`, `.accent-orchid`,
487
+ `.accent-lime`, `.sub`, `.display`, `.lead`, `.caption.on-dark`, `.card-text`,
488
+ `table.dense`.
489
+
490
+ ---
491
+
492
+ ## 12 · Bundling (single-file export)
493
+
494
+ `bundle.mjs` (Vite + vite-plugin-singlefile) crawls a deck's `<link>` and
495
+ `<script type="module">` references, bundles and inlines everything (Lit
496
+ included) into one self-contained HTML file:
497
+
498
+ ```bash
499
+ node bundle.mjs my-talk/index.html # → my-talk/index.bundle.html
500
+ node bundle.mjs my-talk/index.html out.html # explicit output
501
+ node bundle.mjs my-talk/index.html - # to stdout
502
+ node bundle.mjs my-talk/index.html --no-fonts # strip Google Fonts @import (system fonts, zero network)
503
+ ```
504
+
505
+ The bundler resolves rikiki references written with the `rikiki/(dist|themes|tokens.css)`
506
+ path convention (see the §9 caveat about decks that use plain relative paths).
507
+
508
+ ---
509
+
510
+ ## 13 · Recipes / cookbook
511
+
512
+ Copy-paste patterns. Every tag/attribute used here is defined above · combine
513
+ them freely. Assume the deck head loads the theme then `dist/index.js` (§2).
514
+
515
+ ### Markdown + code feature slide
516
+
517
+ ```html
518
+ <deck-feature eyebrow="Module">
519
+ <h1 slot="title">Side effects</h1>
520
+ <p slot="lead" class="lead">A module can <span class="accent">act</span> on import.</p>
521
+ <deck-code lang="ts" hero>
522
+ import './polyfill'; // executed at import time
523
+ </deck-code>
524
+ </deck-feature>
525
+ ```
526
+
527
+ ### Side-by-side comparison (two columns)
528
+
529
+ ```html
530
+ <deck-split eyebrow="ESM">
531
+ <h1 slot="title">Static vs dynamic</h1>
532
+ <deck-card slot="left" color="yellow">
533
+ <h3>Static</h3>
534
+ <deck-md>Resolved at startup. **Tree-shakable.**</deck-md>
535
+ </deck-card>
536
+ <deck-card slot="right" color="green">
537
+ <h3>Dynamic</h3>
538
+ <deck-md>Loaded on demand. *Asynchronous.*</deck-md>
539
+ </deck-card>
540
+ </deck-split>
541
+ ```
542
+
543
+ ### Three columns
544
+
545
+ ```html
546
+ <deck-split eyebrow="Actions" cols="3">
547
+ <h1 slot="title">Three levers</h1>
548
+ <deck-card slot="a" color="yellow"><h3>① Cache</h3><deck-md>…</deck-md></deck-card>
549
+ <deck-card slot="b" color="orange"><h3>② Batch</h3><deck-md>…</deck-md></deck-card>
550
+ <deck-card slot="c" color="green"><h3>③ Defer</h3><deck-md>…</deck-md></deck-card>
551
+ </deck-split>
552
+ ```
553
+
554
+ ### Hero block + two detail cards
555
+
556
+ ```html
557
+ <deck-feature-cards eyebrow="Pipeline">
558
+ <h1 slot="title">How it flows</h1>
559
+ <deck-mermaid>graph LR; A-->B-->C</deck-mermaid>
560
+ <deck-card slot="left" color="yellow"><h3>Ingest</h3><deck-md>…</deck-md></deck-card>
561
+ <deck-card slot="right" color="green"><h3>Serve</h3><deck-md>…</deck-md></deck-card>
562
+ </deck-feature-cards>
563
+ ```
564
+
565
+ ### Big-number stat
566
+
567
+ ```html
568
+ <deck-feature eyebrow="Impact">
569
+ <h1 slot="title">The result</h1>
570
+ <deck-stat num="92%" tone="green">
571
+ <h3 slot="claim">faster cold start</h3>
572
+ after the lazy-import refactor
573
+ </deck-stat>
574
+ </deck-feature>
575
+ ```
576
+
577
+ ### Metric list
578
+
579
+ ```html
580
+ <deck-metric-list>
581
+ <deck-metric severity="bad" value="2.4 s">p95 latency (before)</deck-metric>
582
+ <deck-metric severity="ok" value="0.3 s" mono>p95 latency (after)</deck-metric>
583
+ </deck-metric-list>
584
+ ```
585
+
586
+ ### Callout
587
+
588
+ ```html
589
+ <deck-callout type="warn">
590
+ <deck-md>Don't ship `?live` in a presented deck.</deck-md>
591
+ </deck-callout>
592
+ ```
593
+
594
+ ### Tier ladder
595
+
596
+ ```html
597
+ <deck-tier-list>
598
+ <deck-tier name="LLInt" speed="×1" severity="muted">Bytecode interpreter</deck-tier>
599
+ <deck-tier-arrow>warms up after ~6 calls</deck-tier-arrow>
600
+ <deck-tier name="DFG" speed="×8" severity="ok">Optimizing JIT</deck-tier>
601
+ <deck-tier name="FTL" speed="×100" severity="hot" hot>Top-tier JIT</deck-tier>
602
+ </deck-tier-list>
603
+ ```
604
+
605
+ ### Keyboard-shortcut grid
606
+
607
+ ```html
608
+ <deck-shortcut-list cols="1" col-gap="4">
609
+ <deck-shortcut keys="⌘ K" label="Command palette" tone="accent"></deck-shortcut>
610
+ <deck-shortcut keys="⌘ ⇧ P" label="Run task"></deck-shortcut>
611
+ </deck-shortcut-list>
612
+ ```
613
+
614
+ ### Stepped code reveal (no plugin)
615
+
616
+ ```html
617
+ <deck-feature eyebrow="Build-up">
618
+ <h1 slot="title">One line at a time</h1>
619
+ <deck-code lang="ts" hero step-groups='[[1],[2,3],[4]]'>
620
+ const a = load();
621
+ const b = transform(a);
622
+ const c = render(b);
623
+ export default c;
624
+ </deck-code>
625
+ </deck-feature>
626
+ ```
627
+
628
+ The `step-groups` array sets the slide's step count automatically (3 steps here).
629
+
630
+ ### Stepped blocks (no plugin)
631
+
632
+ ```html
633
+ <deck-feature steps="2" eyebrow="Reveal">
634
+ <h1 slot="title">Two beats</h1>
635
+ <p>Always visible.</p>
636
+ <p data-step-block>Appears on step 1.</p>
637
+ <p data-step-block>Appears on step 2.</p>
638
+ </deck-feature>
639
+ ```
640
+
641
+ ### Per-element click reveals (click-stages plugin)
642
+
643
+ ```html
644
+ <!-- once, after dist/index.js -->
645
+ <script type="module">
646
+ import { installClickStages } from './dist/click-stages.js';
647
+ installClickStages();
648
+ </script>
649
+
650
+ <deck-feature eyebrow="Build">
651
+ <h1 slot="title">Click through</h1>
652
+ <p data-click data-anim="slide-up">First.</p>
653
+ <p data-click-auto="500">Follows automatically after 500 ms.</p>
654
+ <ul data-click-stagger="80" data-anim="slide-up">
655
+ <li>wave 1</li><li>wave 2</li><li>wave 3</li>
656
+ </ul>
657
+ </deck-feature>
658
+ ```
659
+
660
+ ### Magic Move (morph across slides)
661
+
662
+ ```html
663
+ <deck-feature><h1 slot="title">Before</h1>
664
+ <deck-code lang="ts" data-morph="snippet">const x = 1;</deck-code>
665
+ </deck-feature>
666
+ <deck-feature><h1 slot="title">After</h1>
667
+ <deck-code lang="ts" data-morph="snippet">const x = compute();</deck-code>
668
+ </deck-feature>
669
+ ```
670
+
671
+ The matching `data-morph="snippet"` glides/resizes the element from the first
672
+ slide to the second.
673
+
674
+ ### Speaker notes
675
+
676
+ ```html
677
+ <deck-feature>
678
+ <h1 slot="title">My slide</h1>
679
+ <deck-notes>
680
+ - Mention the migration story
681
+ - Pause on the Java joke
682
+ </deck-notes>
683
+ </deck-feature>
684
+ ```
685
+
686
+ ### Full-bleed photo with caption
687
+
688
+ ```html
689
+ <deck-photo src="./hero.jpg" position="center" darken="0.5" align="bottom">
690
+ <h1>Scale</h1>
691
+ <p class="sub">3 M req/s at peak</p>
692
+ </deck-photo>
693
+ ```
694
+
695
+ ### Retheme one slide locally
696
+
697
+ ```html
698
+ <deck-stat num="∞" style="--rik-accent: #ff0066;">
699
+ <h3 slot="claim">possibilities</h3>
700
+ </deck-stat>
701
+ ```
702
+
703
+ ---
704
+
705
+ ## 14 · Authoring rules for LLMs
706
+
707
+ - **Never nest `<deck-root>`.** One per document.
708
+ - **Load theme CSS before `dist/index.js`.**
709
+ - Every direct child of `<deck-root>` is one slide; keep **one focal idea per
710
+ slide**.
711
+ - **Set `slot=` wherever a layout defines named slots** (e.g. `slot="title"`,
712
+ `slot="lead"`, `slot="left"`/`slot="right"`, `slot="a"`/`b`/`c`). Content with
713
+ no matching slot lands in the default slot.
714
+ - Prefer **`deck-md`** for prose; use **`deck-code`** for code.
715
+ - Use **semantic `--rik-*` tokens** for any color/spacing override, at `:root`
716
+ (or component `--deck-*-…` tokens on one host). Do not hardcode colors.
717
+ - Move detail into **`<deck-notes>`** rather than crowding the slide.
718
+ - Only use tags, attributes, and tokens listed in this document.