@depths/waves 0.2.0 → 0.5.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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @depths/waves (v0.2.0)
1
+ # @depths/waves (v0.5.0)
2
2
 
3
3
  `@depths/waves` is a TypeScript-first library + CLI for rendering videos from a JSON "intermediate representation" (IR) using Remotion.
4
4
 
@@ -8,11 +8,25 @@ The intended workflow is:
8
8
  2. The IR is validated (schema + semantics + registry contracts).
9
9
  3. The IR is rendered to an output video file (MP4 by default) using Remotion.
10
10
 
11
- v0.2.0 adds a shadcn-like catalog of higher-level "composite" components and a hybrid IR that supports:
11
+ v0.2.0 introduced a shadcn-like catalog of higher-level "composite" components and a hybrid IR that supports:
12
12
 
13
13
  - **Segments (recommended for agents):** sequential scenes with optional overlaps ("transitions")
14
14
  - **Timeline (escape hatch):** explicit timed nodes with overlaps allowed
15
15
 
16
+ v0.4.0 focuses on **unified aesthetics control**:
17
+
18
+ - **Theme tokens:** shadcn-ish design tokens (colors, typography, spacing, radii, shadows, motion) declared once at the video level
19
+ - **Presets:** built-in theme presets for fast, consistent styling
20
+ - **Token-only styling:** components reference token keys (no hardcoded styling constants in primitives or composites)
21
+ - **Visual debugging workflow:** `waves stills` and `--debugBounds/--debugLabels` to inspect alignment issues without pixel tests
22
+
23
+ v0.5.0 focuses on **robustness and performance**:
24
+
25
+ - **Faster agent loops:** deterministic bundle caching (`.waves-cache/`) so repeated renders don't re-bundle
26
+ - **Fail-fast assets:** local `/assets/...` references are checked before expensive renders
27
+ - **Deterministic font loading:** theme fonts must load successfully or the render is cancelled
28
+ - **Stricter transitions:** invalid transition types/props are rejected (no silent overlaps)
29
+
16
30
  ## Table of contents
17
31
 
18
32
  - [Concept (end-to-end)](#concept-end-to-end)
@@ -24,7 +38,7 @@ v0.2.0 adds a shadcn-like catalog of higher-level "composite" components and a h
24
38
  - [Rendering model](#rendering-model)
25
39
  - [Installation](#installation)
26
40
  - [CLI (agent workflow)](#cli-agent-workflow)
27
- - [IR v2.0 (authoring contract)](#ir-v20-authoring-contract)
41
+ - [IR v3.0 (authoring contract)](#ir-v30-authoring-contract)
28
42
  - [Components (primitives + composites)](#components-primitives--composites)
29
43
  - [Examples (composition recipes)](#examples-composition-recipes)
30
44
  - [Library API (quickstart)](#library-api-quickstart)
@@ -62,7 +76,7 @@ At render time, Waves is "just React + Remotion". The strictness lives earlier (
62
76
  The codebase has a small number of core concepts:
63
77
 
64
78
  - **Video IR (`VideoIRSchema`)**: the JSON document format you author.
65
- - v0.2.0 targets `version: "2.0"` only.
79
+ - v0.4.0+ targets `version: "3.0"` only.
66
80
  - You author exactly one of `segments[]` (recommended) or `timeline[]` (escape hatch).
67
81
  - **Component Registry (`ComponentRegistry`)**: a map from string `type` -> React component + Zod props schema + metadata.
68
82
  - Only registered components can render.
@@ -99,7 +113,7 @@ Why the internal `Segment` wrapper exists:
99
113
  Overlap math:
100
114
 
101
115
  - Let segment `i` have duration `Di`.
102
- - Let overlap `Oi` be `segments[i].transitionToNext.durationInFrames` (only for segments that have a next segment).
116
+ - Let overlap `Oi` (in frames) be `theme.motion.durationsInFrames[segments[i].transitionToNext.duration]` (only for segments that have a next segment).
103
117
  - Then the compiled start time is:
104
118
  - `start(0) = 0`
105
119
  - `start(i+1) = start(i) + Di - Oi`
@@ -178,9 +192,9 @@ For an agent that can execute terminal commands, the recommended flow is:
178
192
  1. `waves prompt --format json --out waves-prompt.json`
179
193
  2. Use `waves-prompt.json.systemPrompt` + `waves-prompt.json.schemas` + `waves-prompt.json.catalog` as the authoring contract for the model
180
194
  3. Have the model output a single JSON object (Video IR)
181
- 4. Write that JSON to `video.v2.json`
182
- 5. `waves validate --in video.v2.json` until it passes
183
- 6. `waves render --in video.v2.json --out output.mp4 ...`
195
+ 4. Write that JSON to `video.v3.json`
196
+ 5. `waves validate --in video.v3.json` until it passes
197
+ 6. `waves render --in video.v3.json --out output.mp4 ...`
184
198
 
185
199
  ### 1) Get the prompt payload (system prompt + schemas + catalog)
186
200
 
@@ -214,7 +228,7 @@ If you register custom components at module import time, include them with repea
214
228
  ```bash
215
229
  npx waves prompt --format json --register ./src/register-waves-components.ts
216
230
  npx waves schema --kind components --register ./src/register-waves-components.ts
217
- npx waves validate --in ./video.v2.json --register ./src/register-waves-components.ts
231
+ npx waves validate --in ./video.v3.json --register ./src/register-waves-components.ts
218
232
  ```
219
233
 
220
234
  Important notes on `--register`:
@@ -227,31 +241,31 @@ Important notes on `--register`:
227
241
  ### 2) Write IR JSON
228
242
 
229
243
  ```bash
230
- npx waves write-ir --template basic --pretty --out ./video.v2.json
244
+ npx waves write-ir --template basic --pretty --out ./video.v3.json
231
245
  ```
232
246
 
233
247
  ### 3) Validate IR JSON
234
248
 
235
249
  ```bash
236
- npx waves validate --in ./video.v2.json
250
+ npx waves validate --in ./video.v3.json
237
251
  ```
238
252
 
239
253
  Structured validation result:
240
254
 
241
255
  ```bash
242
- npx waves validate --in ./video.v2.json --format json
256
+ npx waves validate --in ./video.v3.json --format json
243
257
  ```
244
258
 
245
259
  ### 4) Render MP4
246
260
 
247
261
  ```bash
248
- npx waves render --in ./video.v2.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
262
+ npx waves render --in ./video.v3.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
249
263
  ```
250
264
 
251
265
  If your IR references `"/assets/..."` paths, pass `--publicDir` and ensure the files exist at `${publicDir}/assets/...`:
252
266
 
253
267
  ```bash
254
- npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public
268
+ npx waves render --in ./video.v3.json --out ./output.mp4 --publicDir ./public
255
269
  ```
256
270
 
257
271
  ### Exit codes
@@ -273,7 +287,7 @@ Prints a short help text.
273
287
 
274
288
  #### `waves --version`
275
289
 
276
- Prints the package version (e.g. `0.2.0`).
290
+ Prints the package version (e.g. `0.5.0`).
277
291
 
278
292
  #### `waves prompt`
279
293
 
@@ -336,21 +350,42 @@ npx waves catalog
336
350
  npx waves catalog --format json --pretty --out ./waves-catalog.json
337
351
  ```
338
352
 
353
+ #### `waves themes`
354
+
355
+ Lists built-in theme presets (or prints the full token object for a preset).
356
+
357
+ Flags:
358
+
359
+ - `--format text|json` (default `text`)
360
+ - `--id <preset>` (optional; when set, prints the preset tokens instead of listing preset ids)
361
+ - `--pretty` (json format only)
362
+ - `--out <path>` (optional)
363
+
364
+ Examples:
365
+
366
+ ```bash
367
+ npx waves themes
368
+ npx waves themes --format json --pretty --out ./waves-themes.json
369
+ npx waves themes --id studio-dark --format json --pretty --out ./studio-dark.tokens.json
370
+ ```
371
+
339
372
  #### `waves write-ir`
340
373
 
341
- Writes a starter IR JSON file (always `version: "2.0"` segments mode).
374
+ Writes a starter IR JSON file (always `version: "3.0"` segments mode).
342
375
 
343
376
  Flags:
344
377
 
345
378
  - `--template minimal|basic` (default `minimal`)
379
+ - `--preset <id>` (optional; default `studio-dark`)
380
+ - `--themeOverrides <path>` (optional; JSON theme override object merged over the preset)
346
381
  - `--pretty`
347
382
  - `--out <path>` (required)
348
383
 
349
384
  Examples:
350
385
 
351
386
  ```bash
352
- npx waves write-ir --template minimal --pretty --out ./video.v2.json
353
- npx waves write-ir --template basic --pretty --out ./examples/basic.v2.json
387
+ npx waves write-ir --template minimal --pretty --out ./video.v3.json
388
+ npx waves write-ir --template basic --pretty --out ./examples/basic.v3.json
354
389
  ```
355
390
 
356
391
  #### `waves validate`
@@ -362,6 +397,8 @@ Flags:
362
397
  - `--in <path>` (required)
363
398
  - `--format text|json` (default `text`)
364
399
  - `--pretty` (json format only)
400
+ - `--preset <id>` (optional; overrides the IR theme preset id)
401
+ - `--themeOverrides <path>` (optional; JSON theme override object merged over IR theme overrides)
365
402
  - `--register <module>` (repeatable)
366
403
 
367
404
  Behavior:
@@ -372,8 +409,8 @@ Behavior:
372
409
  Examples:
373
410
 
374
411
  ```bash
375
- npx waves validate --in ./video.v2.json
376
- npx waves validate --in ./video.v2.json --format json --pretty
412
+ npx waves validate --in ./video.v3.json
413
+ npx waves validate --in ./video.v3.json --format json --pretty
377
414
  ```
378
415
 
379
416
  #### `waves render`
@@ -388,19 +425,49 @@ Flags:
388
425
  - `--codec h264|h265|vp8|vp9` (optional; default `h264`)
389
426
  - `--crf <n>` (optional; forwarded to Remotion renderer)
390
427
  - `--concurrency <n|string>` (optional; forwarded to Remotion renderer)
428
+ - `--preset <id>` (optional; overrides the IR theme preset id)
429
+ - `--themeOverrides <path>` (optional; JSON theme override object merged over IR theme overrides)
430
+ - `--debugBounds` (optional; draws debug outlines for every rendered node)
431
+ - `--debugLabels` (optional; labels debug outlines with `type#id`)
391
432
  - `--register <module>` (repeatable)
392
433
  - `--pretty` (only affects the formatting of error JSON when render fails)
393
434
 
394
435
  Examples:
395
436
 
396
437
  ```bash
397
- npx waves render --in ./video.v2.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
398
- npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public
438
+ npx waves render --in ./video.v3.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
439
+ npx waves render --in ./video.v3.json --out ./output.mp4 --publicDir ./public
440
+ npx waves render --in ./video.v3.json --out ./output.mp4 --publicDir ./public --debugBounds --debugLabels
441
+ ```
442
+
443
+ #### `waves stills`
444
+
445
+ Renders a set of still images (single frames) from an IR JSON file. This is the recommended way to iterate on alignment and overlays without re-rendering full MP4s.
446
+
447
+ Flags:
448
+
449
+ - `--in <path>` (required)
450
+ - `--outDir <path>` (required)
451
+ - `--frames <csv>` (required; e.g. `"0,30,60"`)
452
+ - `--publicDir <path>` (optional; required if your IR uses `/assets/...` paths)
453
+ - `--imageFormat png|jpeg|webp` (optional; default `png`)
454
+ - `--scale <n>` (optional; default `1`)
455
+ - `--jpegQuality <n>` (optional; default `90`; only used when `--imageFormat jpeg`)
456
+ - `--preset <id>` (optional; overrides the IR theme preset id)
457
+ - `--themeOverrides <path>` (optional; JSON theme override object merged over IR theme overrides)
458
+ - `--debugBounds` / `--debugLabels` (same as `waves render`)
459
+ - `--register <module>` (repeatable)
460
+
461
+ Examples:
462
+
463
+ ```bash
464
+ npx waves stills --in ./video.v3.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public
465
+ npx waves stills --in ./video.v3.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public --debugBounds --debugLabels
399
466
  ```
400
467
 
401
- ## IR v2.0 (authoring contract)
468
+ ## IR v3.0 (authoring contract)
402
469
 
403
- v0.2.0 targets `version: "2.0"` only.
470
+ v0.4.0+ targets `version: "3.0"` only.
404
471
 
405
472
  ### Recommended: `segments[]` (high-level)
406
473
 
@@ -408,18 +475,19 @@ In segments mode, you provide sequential segments and Waves compiles them into a
408
475
 
409
476
  Key rules:
410
477
 
411
- - Segment overlap is controlled by `transitionToNext.durationInFrames`.
478
+ - Segment overlap is controlled by `transitionToNext.duration` (a theme motion token key like `"fast"`, `"base"`, `"slow"`).
412
479
  - `transitionToNext` is only valid when there is a "next" segment (i.e. it is not allowed on the last segment).
413
480
  - The total video duration must match the compiled timeline end:
414
481
  - `video.durationInFrames = sum(segment.durationInFrames) - sum(overlap)`
415
- - where `overlap = transitionToNext.durationInFrames` for each segment that has a next segment.
482
+ - where `overlap = theme.motion.durationsInFrames[transitionToNext.duration]` for each segment that has a next segment (after resolving the theme preset + overrides).
416
483
 
417
484
  Minimal example:
418
485
 
419
486
  ```json
420
487
  {
421
- "version": "2.0",
488
+ "version": "3.0",
422
489
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 60 },
490
+ "theme": { "preset": "studio-dark" },
423
491
  "segments": [
424
492
  {
425
493
  "id": "scene-1",
@@ -427,7 +495,7 @@ Minimal example:
427
495
  "root": {
428
496
  "id": "root",
429
497
  "type": "Scene",
430
- "props": { "background": { "type": "color", "value": "#000000" } },
498
+ "props": { "background": { "type": "color", "token": "background" } },
431
499
  "children": [{ "id": "t1", "type": "Text", "props": { "content": "Hello" } }]
432
500
  }
433
501
  }
@@ -438,7 +506,7 @@ Minimal example:
438
506
  Supported `transitionToNext.type` values (segment overlap transitions):
439
507
 
440
508
  - `FadeTransition`
441
- - `SlideTransition` (`props`: `{ direction: "left"|"right"|"up"|"down", distance?: number }`)
509
+ - `SlideTransition` (`props`: `{ direction: "left"|"right"|"up"|"down", distance?: "enter"|"nudge"|"travel" }`)
442
510
  - `ZoomTransition` (`props`: `{ type: "zoomIn"|"zoomOut" }`)
443
511
  - `WipeTransition` (`props`: `{ direction: "left"|"right"|"up"|"down"|"diagonal", softEdge?: boolean }`)
444
512
  - `CircularReveal` (`props`: `{ direction: "open"|"close", center?: { x: 0..1, y: 0..1 } }`)
@@ -454,14 +522,15 @@ Notes:
454
522
 
455
523
  ```json
456
524
  {
457
- "version": "2.0",
525
+ "version": "3.0",
458
526
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 60 },
527
+ "theme": { "preset": "studio-dark" },
459
528
  "timeline": [
460
529
  {
461
530
  "id": "scene",
462
531
  "type": "Scene",
463
532
  "timing": { "from": 0, "durationInFrames": 60 },
464
- "props": { "background": { "type": "color", "value": "#000000" } }
533
+ "props": { "background": { "type": "color", "token": "background" } }
465
534
  }
466
535
  ]
467
536
  }
@@ -559,10 +628,13 @@ node scripts/generate-readme-components.mjs
559
628
  | `ImageSequence` | composite | image | no | no | Plays a numbered image sequence (frame-by-frame) |
560
629
  | `ImageWithCaption` | composite | image | no | no | Image with a caption strip (top/bottom) or overlay caption |
561
630
  | `KenBurnsImage` | composite | image | no | no | Slow zoom and pan (Ken Burns effect) for a still image |
562
- | `Box` | primitive | layout | yes | no | Positioned container box for layout and backgrounds |
631
+ | `Box` | primitive | layout | yes | no | Flow container for layout and backgrounds (layout-safe) |
563
632
  | `CardStack` | composite | layout | no | no | Sequential stacked cards (2-5) with flip/slide/fade transitions |
633
+ | `Frame` | primitive | layout | yes | no | Absolute-positioned container (x/y placement) |
564
634
  | `Grid` | primitive | layout | yes | no | Grid layout container with configurable rows/columns |
565
635
  | `GridLayout` | composite | layout | yes (1..∞) | no | Simple responsive grid layout for child components |
636
+ | `Layer` | primitive | layout | yes (1..∞) | no | One overlay layer with explicit zIndex inside Layers |
637
+ | `Layers` | primitive | layout | yes (1..∞) | no | Overlay container for stacking children (use Layer for zIndex) |
566
638
  | `Scene` | primitive | layout | yes | no | Scene container with a background and nested children |
567
639
  | `Segment` | primitive | layout | yes (1..1) | yes | Internal segment wrapper (used by v2 segments compiler) |
568
640
  | `Shape` | primitive | layout | no | no | Simple rect/circle shape for UI accents |
@@ -586,7 +658,7 @@ node scripts/generate-readme-components.mjs
586
658
  | `TypewriterText` | composite | text | no | no | Character-by-character text reveal with optional blinking cursor |
587
659
  | `CircularReveal` | composite | transition | yes (1..∞) | no | Circular iris reveal/hide transition wrapper |
588
660
  | `FadeTransition` | composite | transition | yes (1..∞) | no | Fade in/out wrapper (used for segment transitions and overlays) |
589
- | `SlideTransition` | composite | transition | yes (1..∞) | no | Slide in/out wrapper (used for segment transitions and overlays) |
661
+ | `SlideTransition` | composite | transition | yes (1..∞) | no | Slide in/out wrapper transition |
590
662
  | `WipeTransition` | composite | transition | yes (1..∞) | no | Directional wipe reveal/hide wrapper transition |
591
663
  | `ZoomTransition` | composite | transition | yes (1..∞) | no | Zoom in/out wrapper transition |
592
664
 
@@ -601,18 +673,25 @@ node scripts/generate-readme-components.mjs
601
673
  - internal: `false`
602
674
  - children: `no`
603
675
  - description: Branded intro scene (logo + company name + optional tagline)
604
- - llmGuidance: Use as the first segment. Works best at 3-5 seconds. musicTrack can add ambience.
676
+ - llmGuidance: Use as the first segment. Works best at 3-5 seconds. Uses theme tokens (no raw styling). musicTrack can add ambience.
605
677
 
606
678
  Props:
607
679
 
608
680
  | Prop | Type | Required | Default | Notes |
609
681
  | - | - | - | - | - |
610
- | `backgroundColor` | string | yes | "#000000" | |
682
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
611
683
  | `companyName` | string | yes | | minLength=1 |
684
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
685
+ | `logoSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "xl" | |
612
686
  | `logoSrc` | string | yes | | minLength=1 |
613
687
  | `musicTrack` | string | no | | |
614
- | `primaryColor` | string | yes | "#FFFFFF" | |
688
+ | `nameColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
689
+ | `nameSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
690
+ | `nameWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
615
691
  | `tagline` | string | no | | |
692
+ | `taglineColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "mutedForeground" | |
693
+ | `taglineSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
694
+ | `taglineWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
616
695
 
617
696
  ##### `LogoReveal`
618
697
 
@@ -621,15 +700,17 @@ Props:
621
700
  - internal: `false`
622
701
  - children: `no`
623
702
  - description: Logo intro animation (fade/scale/rotate/slide), optionally with a sound effect
624
- - llmGuidance: Use for intros/outros. Keep the logo high-contrast and centered. soundEffect can be a short sting.
703
+ - llmGuidance: Use for intros/outros. Keep the logo high-contrast and centered. Uses theme tokens (no raw styling). soundEffect can be a short sting.
625
704
 
626
705
  Props:
627
706
 
628
707
  | Prop | Type | Required | Default | Notes |
629
708
  | - | - | - | - | - |
630
- | `backgroundColor` | string | yes | "#000000" | |
709
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
631
710
  | `effect` | enum("fade" \| "scale" \| "rotate" \| "slide") | yes | "scale" | |
711
+ | `logoSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "xl" | |
632
712
  | `logoSrc` | string | yes | | minLength=1 |
713
+ | `slideDistance` | enum("enter" \| "nudge" \| "travel") | yes | "travel" | |
633
714
  | `soundEffect` | string | no | | |
634
715
 
635
716
  ##### `OutroScene`
@@ -639,17 +720,30 @@ Props:
639
720
  - internal: `false`
640
721
  - children: `no`
641
722
  - description: End screen with logo, message, optional CTA buttons and social handles
642
- - llmGuidance: Use as the last segment. Keep CTAs <=3 for clarity.
723
+ - llmGuidance: Use as the last segment. Keep CTAs <=3 for clarity. Uses theme tokens (no raw styling).
643
724
 
644
725
  Props:
645
726
 
646
727
  | Prop | Type | Required | Default | Notes |
647
728
  | - | - | - | - | - |
648
- | `backgroundColor` | string | yes | "#000000" | |
729
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
730
+ | `ctaBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
649
731
  | `ctaButtons` | array<object> | no | | maxItems=3 |
732
+ | `ctaColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
733
+ | `ctaRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
734
+ | `ctaTextSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
735
+ | `ctaTextWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
736
+ | `logoSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "lg" | |
650
737
  | `logoSrc` | string | yes | | minLength=1 |
651
738
  | `message` | string | yes | "Thank You" | |
739
+ | `messageColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
740
+ | `messageFont` | enum("display" \| "body" \| "mono") | yes | "display" | |
741
+ | `messageSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
742
+ | `messageWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
743
+ | `socialColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "mutedForeground" | |
652
744
  | `socialHandles` | array<object> | no | | maxItems=4 |
745
+ | `socialTextSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "md" | |
746
+ | `socialTextWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
653
747
 
654
748
  ##### `Watermark`
655
749
 
@@ -658,18 +752,21 @@ Props:
658
752
  - internal: `false`
659
753
  - children: `no`
660
754
  - description: Persistent logo/text watermark in a corner
661
- - llmGuidance: Use subtle opacity (0.3-0.6). bottomRight is standard.
755
+ - llmGuidance: Use subtle opacity. bottomRight is standard. Uses theme tokens (no raw styling).
662
756
 
663
757
  Props:
664
758
 
665
759
  | Prop | Type | Required | Default | Notes |
666
760
  | - | - | - | - | - |
667
- | `color` | string | yes | "#FFFFFF" | |
668
- | `opacity` | number | yes | 0.5 | min=0.1, max=1 |
761
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "mutedForeground" | |
762
+ | `font` | enum("display" \| "body" \| "mono") | yes | "body" | |
763
+ | `opacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "subtle" | |
669
764
  | `position` | enum("topLeft" \| "topRight" \| "bottomLeft" \| "bottomRight") | yes | "bottomRight" | |
670
- | `size` | number | yes | 60 | min=30, max=150 |
765
+ | `size` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
671
766
  | `src` | string | no | | |
672
767
  | `text` | string | no | | |
768
+ | `textSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
769
+ | `textWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
673
770
  | `type` | enum("logo" \| "text") | yes | "logo" | |
674
771
 
675
772
  #### Category: `data`
@@ -681,21 +778,22 @@ Props:
681
778
  - internal: `false`
682
779
  - children: `no`
683
780
  - description: Animated numeric counter (spring or linear), optionally with an icon and suffix
684
- - llmGuidance: Use for big stats. animationType="spring" feels natural. suffix for units (%, K, M).
781
+ - llmGuidance: Use for big stats. animationType="spring" feels natural. suffix for units (%, K, M). Uses theme tokens (no raw styling).
685
782
 
686
783
  Props:
687
784
 
688
785
  | Prop | Type | Required | Default | Notes |
689
786
  | - | - | - | - | - |
690
787
  | `animationType` | enum("spring" \| "linear") | yes | "spring" | |
691
- | `color` | string | yes | "#FFFFFF" | |
692
- | `fontFamily` | string | yes | "Inter" | |
693
- | `fontSize` | number | yes | 96 | min=8, max=300 |
694
- | `fontWeight` | integer | yes | 700 | min=100, max=900 |
788
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
789
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
695
790
  | `from` | number | yes | 0 | |
696
791
  | `icon` | string | no | | |
792
+ | `iconSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
793
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "5xl" | |
697
794
  | `suffix` | string | no | | |
698
795
  | `to` | number | yes | 100 | |
796
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
699
797
 
700
798
  ##### `BarChart`
701
799
 
@@ -704,17 +802,32 @@ Props:
704
802
  - internal: `false`
705
803
  - children: `no`
706
804
  - description: Animated bar chart (vertical or horizontal)
707
- - llmGuidance: Use 2-6 bars. Provide maxValue to lock scale across multiple charts.
805
+ - llmGuidance: Use 2-6 bars. Provide maxValue to lock scale across multiple charts. Uses theme tokens (no raw styling).
708
806
 
709
807
  Props:
710
808
 
711
809
  | Prop | Type | Required | Default | Notes |
712
810
  | - | - | - | - | - |
713
811
  | `data` | array<object> | yes | | minItems=2, maxItems=8 |
812
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
813
+ | `gridColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "border" | |
814
+ | `gridOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "faint" | |
815
+ | `gridRowSize` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
816
+ | `labelColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
817
+ | `labelSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "md" | |
818
+ | `labelWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
714
819
  | `maxValue` | number | no | | |
715
820
  | `orientation` | enum("horizontal" \| "vertical") | yes | "vertical" | |
821
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "24" | |
822
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "sm" | |
716
823
  | `showGrid` | boolean | yes | false | |
717
824
  | `showValues` | boolean | yes | true | |
825
+ | `stagger` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
826
+ | `trackColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
827
+ | `trackRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "full" | |
828
+ | `valueColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
829
+ | `valueSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
830
+ | `valueWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
718
831
 
719
832
  ##### `LineGraph`
720
833
 
@@ -723,18 +836,21 @@ Props:
723
836
  - internal: `false`
724
837
  - children: `no`
725
838
  - description: Animated line graph (SVG) with draw/reveal modes
726
- - llmGuidance: Use 5-20 points. animate="draw" traces the line; animate="reveal" wipes it left-to-right.
839
+ - llmGuidance: Use 5-20 points. animate="draw" traces the line; animate="reveal" wipes it left-to-right. Uses theme tokens (no raw styling).
727
840
 
728
841
  Props:
729
842
 
730
843
  | Prop | Type | Required | Default | Notes |
731
844
  | - | - | - | - | - |
732
845
  | `animate` | enum("draw" \| "reveal") | yes | "draw" | |
733
- | `color` | string | yes | "#00FF00" | |
846
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
734
847
  | `data` | array<object> | yes | | minItems=2, maxItems=50 |
848
+ | `dotRadius` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "2" | |
735
849
  | `fillArea` | boolean | yes | false | |
850
+ | `fillOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "faint" | |
851
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "8" | |
736
852
  | `showDots` | boolean | yes | true | |
737
- | `strokeWidth` | number | yes | 3 | min=1, max=10 |
853
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "1" | |
738
854
 
739
855
  ##### `ProgressBar`
740
856
 
@@ -743,18 +859,25 @@ Props:
743
859
  - internal: `false`
744
860
  - children: `no`
745
861
  - description: Animated progress bar that fills over the component duration
746
- - llmGuidance: Use for loading/countdowns. showPercentage=true is helpful for clarity.
862
+ - llmGuidance: Use for loading/countdowns. showPercentage=true is helpful for clarity. Uses theme tokens (no raw styling).
747
863
 
748
864
  Props:
749
865
 
750
866
  | Prop | Type | Required | Default | Notes |
751
867
  | - | - | - | - | - |
752
- | `backgroundColor` | string | yes | "rgba(255,255,255,0.2)" | |
753
- | `color` | string | yes | "#00FF00" | |
754
- | `height` | number | yes | 10 | min=5, max=50 |
868
+ | `barColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
869
+ | `edgeOffset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "12" | |
870
+ | `height` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "3" | |
871
+ | `insetX` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "20" | |
755
872
  | `label` | string | no | | |
873
+ | `labelColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
874
+ | `labelFont` | enum("display" \| "body" \| "mono") | yes | "body" | |
875
+ | `labelSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "md" | |
876
+ | `labelWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
756
877
  | `position` | enum("top" \| "bottom") | yes | "bottom" | |
878
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "full" | |
757
879
  | `showPercentage` | boolean | yes | true | |
880
+ | `trackColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
758
881
 
759
882
  ##### `ProgressRing`
760
883
 
@@ -763,18 +886,22 @@ Props:
763
886
  - internal: `false`
764
887
  - children: `no`
765
888
  - description: Circular progress indicator (SVG) that animates from 0 to percentage over duration
766
- - llmGuidance: Use for completion and goals. size 160-260 is typical. showLabel displays the percentage.
889
+ - llmGuidance: Use for completion and goals. showLabel displays the percentage. Uses theme tokens (no raw styling).
767
890
 
768
891
  Props:
769
892
 
770
893
  | Prop | Type | Required | Default | Notes |
771
894
  | - | - | - | - | - |
772
- | `backgroundColor` | string | yes | "rgba(255,255,255,0.2)" | |
773
- | `color` | string | yes | "#00FF00" | |
895
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
896
+ | `labelColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
897
+ | `labelFont` | enum("display" \| "body" \| "mono") | yes | "display" | |
898
+ | `labelSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
899
+ | `labelWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
774
900
  | `percentage` | number | yes | | min=0, max=100 |
775
901
  | `showLabel` | boolean | yes | true | |
776
- | `size` | number | yes | 200 | min=100, max=500 |
777
- | `strokeWidth` | number | yes | 20 | min=5, max=50 |
902
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "lg" | |
903
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "5" | |
904
+ | `trackColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
778
905
 
779
906
  #### Category: `image`
780
907
 
@@ -791,7 +918,7 @@ Props:
791
918
 
792
919
  | Prop | Type | Required | Default | Notes |
793
920
  | - | - | - | - | - |
794
- | `borderRadius` | number | yes | 0 | min=0 |
921
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
795
922
  | `fit` | enum("cover" \| "contain") | yes | "cover" | |
796
923
  | `opacity` | number | yes | 1 | min=0, max=1 |
797
924
  | `src` | string | yes | | minLength=1 |
@@ -803,15 +930,28 @@ Props:
803
930
  - internal: `false`
804
931
  - children: `no`
805
932
  - description: Collage of multiple images in a grid/stack/scatter layout with staggered entrances
806
- - llmGuidance: Use 2-6 images for best results. layout="grid" is clean; "scatter" is energetic.
933
+ - llmGuidance: Use 2-6 images for best results. layout="grid" is clean; "scatter" is energetic. Uses theme tokens (no raw styling).
807
934
 
808
935
  Props:
809
936
 
810
937
  | Prop | Type | Required | Default | Notes |
811
938
  | - | - | - | - | - |
939
+ | `captionBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
940
+ | `captionColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
941
+ | `captionFont` | enum("display" \| "body" \| "mono") | yes | "body" | |
942
+ | `captionOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "medium" | |
943
+ | `captionPadding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "3" | |
944
+ | `captionSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "md" | |
945
+ | `captionWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
946
+ | `cardHeight` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "xl" | |
947
+ | `cardWidth` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "2xl" | |
948
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
812
949
  | `images` | array<object> | yes | | minItems=2, maxItems=9 |
813
950
  | `layout` | enum("grid" \| "stack" \| "scatter") | yes | "grid" | |
814
- | `stagger` | integer | yes | 5 | min=2, max=10 |
951
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "20" | |
952
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
953
+ | `shadow` | enum("sm" \| "md" \| "lg") | yes | "lg" | |
954
+ | `stagger` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
815
955
 
816
956
  ##### `ImageReveal`
817
957
 
@@ -855,15 +995,21 @@ Props:
855
995
  - internal: `false`
856
996
  - children: `no`
857
997
  - description: Image with a caption strip (top/bottom) or overlay caption
858
- - llmGuidance: Use overlay for quotes/testimonials over photos. Use bottom for standard captions.
998
+ - llmGuidance: Use overlay for quotes/testimonials over photos. Use bottom for standard captions. Uses theme tokens (no raw styling).
859
999
 
860
1000
  Props:
861
1001
 
862
1002
  | Prop | Type | Required | Default | Notes |
863
1003
  | - | - | - | - | - |
864
1004
  | `caption` | string | yes | | maxLength=200 |
1005
+ | `captionBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
1006
+ | `captionColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1007
+ | `captionFont` | enum("display" \| "body" \| "mono") | yes | "body" | |
1008
+ | `captionPadding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "5" | |
865
1009
  | `captionPosition` | enum("top" \| "bottom" \| "overlay") | yes | "bottom" | |
866
- | `captionStyle` | object | no | | additionalProperties=false |
1010
+ | `captionRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
1011
+ | `captionSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1012
+ | `captionWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
867
1013
  | `src` | string | yes | | minLength=1 |
868
1014
 
869
1015
  ##### `KenBurnsImage`
@@ -873,17 +1019,16 @@ Props:
873
1019
  - internal: `false`
874
1020
  - children: `no`
875
1021
  - description: Slow zoom and pan (Ken Burns effect) for a still image
876
- - llmGuidance: Classic documentary-style motion. startScale 1 -> endScale 1.2 is subtle; add panDirection for extra movement.
1022
+ - llmGuidance: Classic documentary-style motion. mode controls zoom; panDirection adds drift. Uses theme tokens (no raw styling).
877
1023
 
878
1024
  Props:
879
1025
 
880
1026
  | Prop | Type | Required | Default | Notes |
881
1027
  | - | - | - | - | - |
882
- | `endScale` | number | yes | 1.2 | min=1, max=2 |
883
- | `panAmount` | number | yes | 50 | min=0, max=100 |
1028
+ | `mode` | enum("zoomIn" \| "zoomOut" \| "none") | yes | "zoomIn" | |
884
1029
  | `panDirection` | enum("none" \| "left" \| "right" \| "up" \| "down") | yes | "none" | |
1030
+ | `panDistance` | enum("enter" \| "nudge" \| "travel") | yes | "nudge" | |
885
1031
  | `src` | string | yes | | minLength=1 |
886
- | `startScale` | number | yes | 1 | min=1, max=2 |
887
1032
 
888
1033
  #### Category: `layout`
889
1034
 
@@ -893,21 +1038,19 @@ Props:
893
1038
  - category: `layout`
894
1039
  - internal: `false`
895
1040
  - children: `yes`
896
- - description: Positioned container box for layout and backgrounds
897
- - llmGuidance: Use Box to position content by x/y and optionally constrain width/height. Prefer Box+Stack/Grid for layouts.
1041
+ - description: Flow container for layout and backgrounds (layout-safe)
1042
+ - llmGuidance: Use Box as a container inside Grid/Stack. Box participates in layout flow. For x/y positioning, use Frame.
898
1043
 
899
1044
  Props:
900
1045
 
901
1046
  | Prop | Type | Required | Default | Notes |
902
1047
  | - | - | - | - | - |
903
- | `backgroundColor` | string | no | | |
904
- | `borderRadius` | number | yes | 0 | min=0 |
1048
+ | `backgroundColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | no | | |
1049
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
905
1050
  | `height` | number | no | | |
906
1051
  | `opacity` | number | yes | 1 | min=0, max=1 |
907
- | `padding` | number | yes | 0 | min=0 |
1052
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
908
1053
  | `width` | number | no | | |
909
- | `x` | number | yes | 0 | |
910
- | `y` | number | yes | 0 | |
911
1054
 
912
1055
  ##### `CardStack`
913
1056
 
@@ -916,16 +1059,50 @@ Props:
916
1059
  - internal: `false`
917
1060
  - children: `no`
918
1061
  - description: Sequential stacked cards (2-5) with flip/slide/fade transitions
919
- - llmGuidance: Use for steps/features. displayDuration is frames per card.
1062
+ - llmGuidance: Use for steps/features. displayDuration is frames per card. Uses theme tokens (no raw styling).
920
1063
 
921
1064
  Props:
922
1065
 
923
1066
  | Prop | Type | Required | Default | Notes |
924
1067
  | - | - | - | - | - |
1068
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surface" | |
925
1069
  | `cards` | array<object> | yes | | minItems=2, maxItems=5 |
1070
+ | `contentFont` | enum("display" \| "body" \| "mono") | yes | "body" | |
1071
+ | `contentOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "strong" | |
1072
+ | `contentSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1073
+ | `contentWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
926
1074
  | `displayDuration` | integer | yes | 90 | min=30, max=150 |
1075
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
1076
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "lg" | |
1077
+ | `shadow` | enum("sm" \| "md" \| "lg") | yes | "lg" | |
1078
+ | `textColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surfaceForeground" | |
1079
+ | `titleFont` | enum("display" \| "body" \| "mono") | yes | "display" | |
1080
+ | `titleSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
1081
+ | `titleWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
927
1082
  | `transition` | enum("flip" \| "slide" \| "fade") | yes | "flip" | |
928
1083
 
1084
+ ##### `Frame`
1085
+
1086
+ - kind: `primitive`
1087
+ - category: `layout`
1088
+ - internal: `false`
1089
+ - children: `yes`
1090
+ - description: Absolute-positioned container (x/y placement)
1091
+ - llmGuidance: Use Frame for precise pixel placement (x/y). Use Box for normal layout flow inside Grid/Stack.
1092
+
1093
+ Props:
1094
+
1095
+ | Prop | Type | Required | Default | Notes |
1096
+ | - | - | - | - | - |
1097
+ | `backgroundColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | no | | |
1098
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
1099
+ | `height` | number | no | | |
1100
+ | `opacity` | number | yes | 1 | min=0, max=1 |
1101
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
1102
+ | `width` | number | no | | |
1103
+ | `x` | number | yes | 0 | |
1104
+ | `y` | number | yes | 0 | |
1105
+
929
1106
  ##### `Grid`
930
1107
 
931
1108
  - kind: `primitive`
@@ -941,9 +1118,9 @@ Props:
941
1118
  | - | - | - | - | - |
942
1119
  | `align` | enum("start" \| "center" \| "end" \| "stretch") | yes | "stretch" | |
943
1120
  | `columns` | integer | yes | 2 | min=1, max=12 |
944
- | `gap` | number | yes | 24 | min=0 |
1121
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
945
1122
  | `justify` | enum("start" \| "center" \| "end" \| "stretch") | yes | "stretch" | |
946
- | `padding` | number | yes | 0 | min=0 |
1123
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
947
1124
  | `rows` | integer | yes | 1 | min=1, max=12 |
948
1125
 
949
1126
  ##### `GridLayout`
@@ -960,10 +1137,43 @@ Props:
960
1137
  | Prop | Type | Required | Default | Notes |
961
1138
  | - | - | - | - | - |
962
1139
  | `columns` | integer | yes | 2 | min=1, max=4 |
963
- | `gap` | number | yes | 20 | min=0, max=50 |
964
- | `padding` | number | yes | 40 | min=0, max=100 |
1140
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "5" | |
1141
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "10" | |
965
1142
  | `rows` | integer | yes | 2 | min=1, max=4 |
966
1143
 
1144
+ ##### `Layer`
1145
+
1146
+ - kind: `primitive`
1147
+ - category: `layout`
1148
+ - internal: `false`
1149
+ - children: `yes (1..∞)`
1150
+ - description: One overlay layer with explicit zIndex inside Layers
1151
+ - llmGuidance: Use Layer inside Layers to control stacking. Put exactly one child in a Layer (recommended).
1152
+
1153
+ Props:
1154
+
1155
+ | Prop | Type | Required | Default | Notes |
1156
+ | - | - | - | - | - |
1157
+ | `inset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
1158
+ | `opacity` | number | yes | 1 | min=0, max=1 |
1159
+ | `pointerEvents` | enum("none" \| "auto") | yes | "none" | |
1160
+ | `zIndex` | integer | yes | 0 | min=-9007199254740991, max=9007199254740991 |
1161
+
1162
+ ##### `Layers`
1163
+
1164
+ - kind: `primitive`
1165
+ - category: `layout`
1166
+ - internal: `false`
1167
+ - children: `yes (1..∞)`
1168
+ - description: Overlay container for stacking children (use Layer for zIndex)
1169
+ - llmGuidance: Use Layers to stack background/content/overlays. Prefer Layer children with explicit zIndex.
1170
+
1171
+ Props:
1172
+
1173
+ | Prop | Type | Required | Default | Notes |
1174
+ | - | - | - | - | - |
1175
+ | `overflow` | enum("visible" \| "hidden") | yes | "visible" | |
1176
+
967
1177
  ##### `Scene`
968
1178
 
969
1179
  - kind: `primitive`
@@ -1007,12 +1217,13 @@ Props:
1007
1217
 
1008
1218
  | Prop | Type | Required | Default | Notes |
1009
1219
  | - | - | - | - | - |
1010
- | `fill` | string | yes | "#FFFFFF" | |
1220
+ | `fill` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1011
1221
  | `height` | number | yes | 100 | |
1012
1222
  | `opacity` | number | yes | 1 | min=0, max=1 |
1223
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
1013
1224
  | `shape` | enum("rect" \| "circle") | yes | "rect" | |
1014
- | `strokeColor` | string | no | | |
1015
- | `strokeWidth` | number | yes | 0 | min=0 |
1225
+ | `strokeColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | no | | |
1226
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
1016
1227
  | `width` | number | yes | 100 | |
1017
1228
  | `x` | number | yes | 0 | |
1018
1229
  | `y` | number | yes | 0 | |
@@ -1030,10 +1241,12 @@ Props:
1030
1241
 
1031
1242
  | Prop | Type | Required | Default | Notes |
1032
1243
  | - | - | - | - | - |
1033
- | `dividerColor` | string | no | | |
1034
- | `gap` | number | yes | 48 | min=0 |
1244
+ | `dividerColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | no | | |
1245
+ | `dividerOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "subtle" | |
1246
+ | `dividerThickness` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "1" | |
1247
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "12" | |
1035
1248
  | `orientation` | enum("vertical" \| "horizontal") | yes | "vertical" | |
1036
- | `padding` | number | yes | 80 | min=0 |
1249
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "20" | |
1037
1250
  | `split` | number | yes | 0.5 | min=0.1, max=0.9 |
1038
1251
 
1039
1252
  ##### `Stack`
@@ -1051,9 +1264,9 @@ Props:
1051
1264
  | - | - | - | - | - |
1052
1265
  | `align` | enum("start" \| "center" \| "end" \| "stretch") | yes | "center" | |
1053
1266
  | `direction` | enum("row" \| "column") | yes | "column" | |
1054
- | `gap` | number | yes | 24 | min=0 |
1267
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
1055
1268
  | `justify` | enum("start" \| "center" \| "end" \| "between") | yes | "center" | |
1056
- | `padding` | number | yes | 0 | min=0 |
1269
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
1057
1270
 
1058
1271
  ##### `ThirdLowerBanner`
1059
1272
 
@@ -1068,14 +1281,28 @@ Props:
1068
1281
 
1069
1282
  | Prop | Type | Required | Default | Notes |
1070
1283
  | - | - | - | - | - |
1071
- | `accentColor` | string | yes | "#FF0000" | |
1284
+ | `accent` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
1285
+ | `avatarBox` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "md" | |
1286
+ | `avatarSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "sm" | |
1072
1287
  | `avatarSrc` | string | no | | |
1073
- | `backgroundColor` | string | yes | "rgba(0,0,0,0.8)" | |
1288
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
1289
+ | `barWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "2" | |
1290
+ | `inset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "20" | |
1291
+ | `minHeight` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "md" | |
1074
1292
  | `name` | string | yes | | maxLength=50 |
1075
- | `primaryColor` | string | yes | "#FFFFFF" | |
1076
- | `secondaryColor` | string | yes | "#CCCCCC" | |
1293
+ | `nameColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1294
+ | `nameFont` | enum("display" \| "body" \| "mono") | yes | "display" | |
1295
+ | `nameSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "3xl" | |
1296
+ | `nameWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1297
+ | `paddingX` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "8" | |
1298
+ | `paddingY` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
1299
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
1077
1300
  | `showAvatar` | boolean | yes | false | |
1078
1301
  | `title` | string | yes | | maxLength=100 |
1302
+ | `titleColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "mutedForeground" | |
1303
+ | `titleFont` | enum("display" \| "body" \| "mono") | yes | "body" | |
1304
+ | `titleSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
1305
+ | `titleWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1079
1306
 
1080
1307
  #### Category: `media`
1081
1308
 
@@ -1111,7 +1338,7 @@ Props:
1111
1338
 
1112
1339
  | Prop | Type | Required | Default | Notes |
1113
1340
  | - | - | - | - | - |
1114
- | `borderRadius` | number | yes | 0 | min=0 |
1341
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
1115
1342
  | `fit` | enum("cover" \| "contain") | yes | "cover" | |
1116
1343
  | `muted` | boolean | yes | true | |
1117
1344
  | `opacity` | number | yes | 1 | min=0, max=1 |
@@ -1124,7 +1351,7 @@ Props:
1124
1351
  - internal: `false`
1125
1352
  - children: `no`
1126
1353
  - description: Video background with an optional overlay (text/logo/gradient)
1127
- - llmGuidance: Use gradient overlay to improve text readability. Set volume=0 to mute.
1354
+ - llmGuidance: Use gradient overlay to improve text readability. Set volume=0 to mute. Uses theme tokens (no raw styling).
1128
1355
 
1129
1356
  Props:
1130
1357
 
@@ -1144,19 +1371,38 @@ Props:
1144
1371
  - internal: `false`
1145
1372
  - children: `no`
1146
1373
  - description: Instagram story-style layout with profile header, text overlay, and optional sticker
1147
- - llmGuidance: Best with 1080x1920 (9:16). Use backgroundImage + short text + optional sticker for mobile-style content.
1374
+ - llmGuidance: Best with 1080x1920 (9:16). Use backgroundImage + short text + optional sticker for mobile-style content. Uses theme tokens (no raw styling).
1148
1375
 
1149
1376
  Props:
1150
1377
 
1151
1378
  | Prop | Type | Required | Default | Notes |
1152
1379
  | - | - | - | - | - |
1153
- | `backgroundColor` | string | yes | "#000000" | |
1380
+ | `avatarSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "sm" | |
1381
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1154
1382
  | `backgroundImage` | string | no | | |
1383
+ | `headerGap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "4" | |
1155
1384
  | `musicTrack` | string | no | | |
1385
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
1156
1386
  | `profilePic` | string | no | | |
1157
1387
  | `sticker` | enum("none" \| "poll" \| "question" \| "countdown") | yes | "none" | |
1388
+ | `stickerBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surface" | |
1389
+ | `stickerBottom` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "lg" | |
1390
+ | `stickerLeft` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
1391
+ | `stickerPadding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "6" | |
1392
+ | `stickerRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
1393
+ | `stickerTextColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surfaceForeground" | |
1394
+ | `stickerTextSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
1395
+ | `stickerTextWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1396
+ | `stickerWidth` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "2xl" | |
1158
1397
  | `text` | string | no | | maxLength=100 |
1398
+ | `textColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1399
+ | `textMarginTop` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "32" | |
1400
+ | `textSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "3xl" | |
1401
+ | `textWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1159
1402
  | `username` | string | no | | |
1403
+ | `usernameColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1404
+ | `usernameSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1405
+ | `usernameWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1160
1406
 
1161
1407
  ##### `TikTokCaption`
1162
1408
 
@@ -1165,19 +1411,22 @@ Props:
1165
1411
  - internal: `false`
1166
1412
  - children: `no`
1167
1413
  - description: TikTok-style captions with stroke and optional word highlighting
1168
- - llmGuidance: Always keep strokeWidth>=2 for readability. highlightStyle="word" or "bounce" makes captions feel dynamic.
1414
+ - llmGuidance: highlightStyle="word" or "bounce" makes captions feel dynamic. Uses theme tokens (no raw styling).
1169
1415
 
1170
1416
  Props:
1171
1417
 
1172
1418
  | Prop | Type | Required | Default | Notes |
1173
1419
  | - | - | - | - | - |
1174
- | `color` | string | yes | "#FFFFFF" | |
1175
- | `fontSize` | number | yes | 48 | min=12, max=120 |
1420
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1421
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1422
+ | `highlightColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "accent" | |
1176
1423
  | `highlightStyle` | enum("word" \| "bounce" \| "none") | yes | "word" | |
1177
1424
  | `position` | enum("center" \| "bottom") | yes | "center" | |
1178
- | `strokeColor` | string | yes | "#000000" | |
1179
- | `strokeWidth` | number | yes | 3 | min=0, max=10 |
1425
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1426
+ | `strokeColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1427
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "1" | |
1180
1428
  | `text` | string | yes | | maxLength=150 |
1429
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1181
1430
 
1182
1431
  ##### `TwitterCard`
1183
1432
 
@@ -1186,19 +1435,39 @@ Props:
1186
1435
  - internal: `false`
1187
1436
  - children: `no`
1188
1437
  - description: Twitter/X post card layout with author header and optional image
1189
- - llmGuidance: Use for announcements/testimonials. Keep tweet short for readability.
1438
+ - llmGuidance: Use for announcements/testimonials. Keep tweet short for readability. Uses theme tokens (no raw styling).
1190
1439
 
1191
1440
  Props:
1192
1441
 
1193
1442
  | Prop | Type | Required | Default | Notes |
1194
1443
  | - | - | - | - | - |
1195
1444
  | `author` | string | yes | | minLength=1 |
1445
+ | `authorSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1446
+ | `authorWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1447
+ | `avatarSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "sm" | |
1196
1448
  | `avatarSrc` | string | no | | |
1449
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1450
+ | `cardBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surface" | |
1451
+ | `cardText` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surfaceForeground" | |
1452
+ | `font` | enum("display" \| "body" \| "mono") | yes | "body" | |
1453
+ | `gap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "4" | |
1197
1454
  | `handle` | string | yes | | minLength=1 |
1455
+ | `handleSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "lg" | |
1456
+ | `handleWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1198
1457
  | `image` | string | no | | |
1458
+ | `mutedText` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "mutedForeground" | |
1459
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "12" | |
1460
+ | `placeholderAvatarBackground` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
1461
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "lg" | |
1462
+ | `shadow` | enum("sm" \| "md" \| "lg") | yes | "lg" | |
1199
1463
  | `timestamp` | string | no | | |
1464
+ | `timestampSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "md" | |
1465
+ | `timestampWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1200
1466
  | `tweet` | string | yes | | maxLength=280 |
1467
+ | `tweetSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1468
+ | `tweetWeight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1201
1469
  | `verified` | boolean | yes | false | |
1470
+ | `verifiedColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
1202
1471
 
1203
1472
  ##### `YouTubeThumbnail`
1204
1473
 
@@ -1207,18 +1476,26 @@ Props:
1207
1476
  - internal: `false`
1208
1477
  - children: `no`
1209
1478
  - description: YouTube-style thumbnail layout (16:9) with bold title and optional face cutout
1210
- - llmGuidance: Use 1280x720 video size. Keep title short and high-contrast. style="bold" is classic thumbnail.
1479
+ - llmGuidance: Use 1280x720 video size. Keep title short and high-contrast. Uses theme tokens (no raw styling).
1211
1480
 
1212
1481
  Props:
1213
1482
 
1214
1483
  | Prop | Type | Required | Default | Notes |
1215
1484
  | - | - | - | - | - |
1216
- | `accentColor` | string | yes | "#FF0000" | |
1217
1485
  | `backgroundImage` | string | yes | | minLength=1 |
1486
+ | `barHeight` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "4" | |
1487
+ | `barWidth` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "xl" | |
1488
+ | `faceWidth` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "2xl" | |
1489
+ | `overlayColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1490
+ | `overlayOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "medium" | |
1491
+ | `strokeColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1492
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "3" | |
1218
1493
  | `style` | enum("bold" \| "minimal" \| "dramatic") | yes | "bold" | |
1219
1494
  | `subtitle` | string | no | | maxLength=40 |
1495
+ | `subtitleColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
1220
1496
  | `thumbnailFace` | string | no | | |
1221
1497
  | `title` | string | yes | | maxLength=60 |
1498
+ | `titleColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1222
1499
 
1223
1500
  #### Category: `text`
1224
1501
 
@@ -1229,23 +1506,23 @@ Props:
1229
1506
  - internal: `false`
1230
1507
  - children: `no`
1231
1508
  - description: Counts from a start value to an end value with formatting options
1232
- - llmGuidance: Use for metrics. format="currency" adds $, format="percentage" multiplies by 100 and adds %.
1509
+ - llmGuidance: Use for metrics. format="currency" adds $, format="percentage" multiplies by 100 and adds %. Uses theme tokens (no raw styling).
1233
1510
 
1234
1511
  Props:
1235
1512
 
1236
1513
  | Prop | Type | Required | Default | Notes |
1237
1514
  | - | - | - | - | - |
1238
- | `color` | string | yes | "#FFFFFF" | |
1515
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1239
1516
  | `decimals` | integer | yes | 0 | min=0, max=4 |
1240
- | `fontFamily` | string | yes | "Inter" | |
1241
- | `fontSize` | number | yes | 72 | min=8, max=240 |
1242
- | `fontWeight` | integer | yes | 700 | min=100, max=900 |
1517
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1243
1518
  | `format` | enum("integer" \| "decimal" \| "currency" \| "percentage") | yes | "integer" | |
1244
1519
  | `from` | number | yes | 0 | |
1245
1520
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1246
1521
  | `prefix` | string | no | | |
1522
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
1247
1523
  | `suffix` | string | no | | |
1248
1524
  | `to` | number | yes | 100 | |
1525
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1249
1526
 
1250
1527
  ##### `GlitchText`
1251
1528
 
@@ -1254,19 +1531,26 @@ Props:
1254
1531
  - internal: `false`
1255
1532
  - children: `no`
1256
1533
  - description: Cyberpunk-style glitch text with RGB split jitter
1257
- - llmGuidance: Use for tech/error moments. intensity 1-3 subtle, 7-10 extreme. glitchDuration is frames at start.
1534
+ - llmGuidance: Use for tech/error moments. intensity 1-3 subtle, 7-10 extreme. glitchDuration uses theme motion tokens.
1258
1535
 
1259
1536
  Props:
1260
1537
 
1261
1538
  | Prop | Type | Required | Default | Notes |
1262
1539
  | - | - | - | - | - |
1263
- | `color` | string | yes | "#FFFFFF" | |
1540
+ | `channelAColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "secondary" | |
1541
+ | `channelBColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
1542
+ | `channelOffset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "1" | |
1543
+ | `channelOpacity` | enum("faint" \| "subtle" \| "medium" \| "strong") | yes | "strong" | |
1544
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1264
1545
  | `content` | string | yes | | maxLength=100 |
1265
- | `fontFamily` | string | yes | "monospace" | |
1266
- | `fontSize` | number | yes | 72 | min=8, max=240 |
1267
- | `glitchDuration` | integer | yes | 10 | min=5, max=30 |
1546
+ | `font` | enum("display" \| "body" \| "mono") | yes | "mono" | |
1547
+ | `glitchDuration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1268
1548
  | `intensity` | integer | yes | 5 | min=1, max=10 |
1549
+ | `jitterX` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "3" | |
1550
+ | `jitterY` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "2" | |
1269
1551
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1552
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
1553
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1270
1554
 
1271
1555
  ##### `KineticTypography`
1272
1556
 
@@ -1275,15 +1559,15 @@ Props:
1275
1559
  - internal: `false`
1276
1560
  - children: `no`
1277
1561
  - description: Rhythmic single-word kinetic typography driven by a timing array
1278
- - llmGuidance: Provide timing frames for when each word appears. Use emphasis="giant" sparingly for impact.
1562
+ - llmGuidance: Provide timing frames for when each word appears. Use emphasis="giant" sparingly for impact. Uses theme tokens (no raw styling).
1279
1563
 
1280
1564
  Props:
1281
1565
 
1282
1566
  | Prop | Type | Required | Default | Notes |
1283
1567
  | - | - | - | - | - |
1284
- | `color` | string | yes | "#FFFFFF" | |
1285
- | `fontFamily` | string | yes | "Inter" | |
1286
- | `fontSize` | number | yes | 48 | min=12, max=140 |
1568
+ | `baseSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1569
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1570
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1287
1571
  | `timing` | array<integer> | yes | | minItems=1 |
1288
1572
  | `transition` | enum("fade" \| "scale" \| "slideLeft" \| "slideRight") | yes | "scale" | |
1289
1573
  | `words` | array<object> | yes | | minItems=1, maxItems=50 |
@@ -1295,7 +1579,7 @@ Props:
1295
1579
  - internal: `false`
1296
1580
  - children: `no`
1297
1581
  - description: Outlined title text with simple draw/fill animation
1298
- - llmGuidance: Use for bold titles. animation="draw" emphasizes the outline; animation="fill" reveals the fill color.
1582
+ - llmGuidance: Use for bold titles. animation="draw" emphasizes the outline; animation="fill" reveals the fill color. Uses theme tokens (no raw styling).
1299
1583
 
1300
1584
  Props:
1301
1585
 
@@ -1303,13 +1587,13 @@ Props:
1303
1587
  | - | - | - | - | - |
1304
1588
  | `animation` | enum("draw" \| "fill") | yes | "draw" | |
1305
1589
  | `content` | string | yes | | maxLength=50 |
1306
- | `fillColor` | string | yes | "#000000" | |
1307
- | `fontFamily` | string | yes | "Inter" | |
1308
- | `fontSize` | number | yes | 96 | min=8, max=240 |
1309
- | `fontWeight` | integer | yes | 800 | min=100, max=900 |
1310
- | `outlineColor` | string | yes | "#FFFFFF" | |
1590
+ | `fillColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
1591
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1592
+ | `outlineColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1311
1593
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1312
- | `strokeWidth` | number | yes | 3 | min=1, max=10 |
1594
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "5xl" | |
1595
+ | `strokeWidth` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "1" | |
1596
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1313
1597
 
1314
1598
  ##### `SplitText`
1315
1599
 
@@ -1325,13 +1609,14 @@ Props:
1325
1609
  | Prop | Type | Required | Default | Notes |
1326
1610
  | - | - | - | - | - |
1327
1611
  | `animation` | enum("fade" \| "slideUp" \| "slideDown" \| "scale" \| "rotate") | yes | "slideUp" | |
1328
- | `color` | string | yes | "#FFFFFF" | |
1612
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1329
1613
  | `content` | string | yes | | maxLength=200 |
1330
- | `fontFamily` | string | yes | "Inter" | |
1331
- | `fontSize` | number | yes | 48 | min=8, max=200 |
1614
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1332
1615
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1616
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1333
1617
  | `splitBy` | enum("word" \| "letter") | yes | "word" | |
1334
- | `stagger` | integer | yes | 3 | min=1, max=10 |
1618
+ | `stagger` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1619
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1335
1620
 
1336
1621
  ##### `SubtitleText`
1337
1622
 
@@ -1340,21 +1625,24 @@ Props:
1340
1625
  - internal: `false`
1341
1626
  - children: `no`
1342
1627
  - description: Caption/subtitle box with fade in/out and optional highlighted words
1343
- - llmGuidance: Use for narration/captions. highlightWords helps emphasize key terms.
1628
+ - llmGuidance: Use for narration/captions. highlightWords helps emphasize key terms. Uses theme tokens (no raw styling).
1344
1629
 
1345
1630
  Props:
1346
1631
 
1347
1632
  | Prop | Type | Required | Default | Notes |
1348
1633
  | - | - | - | - | - |
1349
- | `backgroundColor` | string | yes | "rgba(0,0,0,0.7)" | |
1350
- | `color` | string | yes | "#FFFFFF" | |
1351
- | `fontFamily` | string | yes | "Inter" | |
1352
- | `fontSize` | number | yes | 36 | min=12, max=80 |
1634
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
1635
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1636
+ | `edgeOffset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "20" | |
1637
+ | `font` | enum("display" \| "body" \| "mono") | yes | "body" | |
1638
+ | `highlightColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "accent" | |
1353
1639
  | `highlightWords` | array<string> | no | | |
1354
- | `maxWidth` | number | yes | 800 | min=200, max=1200 |
1355
- | `padding` | number | yes | 20 | min=0, max=80 |
1640
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "5" | |
1356
1641
  | `position` | enum("top" \| "bottom") | yes | "bottom" | |
1642
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "md" | |
1643
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "xl" | |
1357
1644
  | `text` | string | yes | | maxLength=200 |
1645
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1358
1646
 
1359
1647
  ##### `Text`
1360
1648
 
@@ -1370,10 +1658,15 @@ Props:
1370
1658
  | Prop | Type | Required | Default | Notes |
1371
1659
  | - | - | - | - | - |
1372
1660
  | `animation` | enum("none" \| "fade" \| "slide" \| "zoom") | yes | "fade" | |
1373
- | `color` | string | yes | "#FFFFFF" | |
1661
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1374
1662
  | `content` | string | yes | | |
1375
- | `fontSize` | number | yes | 48 | |
1663
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1664
+ | `lineHeight` | enum("tight" \| "normal" \| "relaxed") | yes | "normal" | |
1376
1665
  | `position` | enum("top" \| "center" \| "bottom" \| "left" \| "right") | yes | "center" | |
1666
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1667
+ | `textAlign` | enum("left" \| "center" \| "right") | yes | "center" | |
1668
+ | `tracking` | enum("tight" \| "normal" \| "wide") | yes | "normal" | |
1669
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1377
1670
 
1378
1671
  ##### `TypewriterText`
1379
1672
 
@@ -1388,14 +1681,15 @@ Props:
1388
1681
 
1389
1682
  | Prop | Type | Required | Default | Notes |
1390
1683
  | - | - | - | - | - |
1391
- | `color` | string | yes | "#FFFFFF" | |
1684
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1392
1685
  | `content` | string | yes | | maxLength=500 |
1393
- | `cursorColor` | string | yes | "#FFFFFF" | |
1394
- | `fontFamily` | string | yes | "Inter" | |
1395
- | `fontSize` | number | yes | 48 | min=8, max=200 |
1686
+ | `cursorColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1687
+ | `font` | enum("display" \| "body" \| "mono") | yes | "body" | |
1396
1688
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1397
1689
  | `showCursor` | boolean | yes | true | |
1690
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1398
1691
  | `speed` | number | yes | 2 | min=0.5, max=5 |
1692
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1399
1693
 
1400
1694
  #### Category: `transition`
1401
1695
 
@@ -1406,7 +1700,7 @@ Props:
1406
1700
  - internal: `false`
1407
1701
  - children: `yes (1..∞)`
1408
1702
  - description: Circular iris reveal/hide transition wrapper
1409
- - llmGuidance: direction="open" reveals from center, direction="close" hides to a point. center controls origin.
1703
+ - llmGuidance: direction controls reveal vs hide; center controls origin. duration uses theme motion tokens.
1410
1704
 
1411
1705
  Props:
1412
1706
 
@@ -1414,7 +1708,7 @@ Props:
1414
1708
  | - | - | - | - | - |
1415
1709
  | `center` | object | no | | additionalProperties=false |
1416
1710
  | `direction` | enum("open" \| "close") | yes | "open" | |
1417
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1711
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1418
1712
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1419
1713
 
1420
1714
  ##### `FadeTransition`
@@ -1424,13 +1718,13 @@ Props:
1424
1718
  - internal: `false`
1425
1719
  - children: `yes (1..∞)`
1426
1720
  - description: Fade in/out wrapper (used for segment transitions and overlays)
1427
- - llmGuidance: Use for gentle transitions. durationInFrames ~30 is standard.
1721
+ - llmGuidance: Use for gentle transitions. duration uses theme motion tokens.
1428
1722
 
1429
1723
  Props:
1430
1724
 
1431
1725
  | Prop | Type | Required | Default | Notes |
1432
1726
  | - | - | - | - | - |
1433
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1727
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1434
1728
  | `easing` | enum("linear" \| "easeIn" \| "easeOut" \| "easeInOut") | yes | "easeInOut" | |
1435
1729
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1436
1730
 
@@ -1440,16 +1734,16 @@ Props:
1440
1734
  - category: `transition`
1441
1735
  - internal: `false`
1442
1736
  - children: `yes (1..∞)`
1443
- - description: Slide in/out wrapper (used for segment transitions and overlays)
1444
- - llmGuidance: Use for more dynamic transitions. direction controls where content enters from.
1737
+ - description: Slide in/out wrapper transition
1738
+ - llmGuidance: Use for directional movement. duration and distance use theme motion tokens.
1445
1739
 
1446
1740
  Props:
1447
1741
 
1448
1742
  | Prop | Type | Required | Default | Notes |
1449
1743
  | - | - | - | - | - |
1450
1744
  | `direction` | enum("left" \| "right" \| "up" \| "down") | yes | "left" | |
1451
- | `distance` | integer | yes | 160 | min=1, max=2000 |
1452
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1745
+ | `distance` | enum("enter" \| "nudge" \| "travel") | yes | "travel" | |
1746
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1453
1747
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1454
1748
 
1455
1749
  ##### `WipeTransition`
@@ -1459,14 +1753,14 @@ Props:
1459
1753
  - internal: `false`
1460
1754
  - children: `yes (1..∞)`
1461
1755
  - description: Directional wipe reveal/hide wrapper transition
1462
- - llmGuidance: Use as a more stylized reveal. softEdge can make it feel less harsh.
1756
+ - llmGuidance: Use as a more stylized reveal. softEdge can make it feel less harsh. duration uses theme motion tokens.
1463
1757
 
1464
1758
  Props:
1465
1759
 
1466
1760
  | Prop | Type | Required | Default | Notes |
1467
1761
  | - | - | - | - | - |
1468
1762
  | `direction` | enum("left" \| "right" \| "up" \| "down" \| "diagonal") | yes | "right" | |
1469
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1763
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1470
1764
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1471
1765
  | `softEdge` | boolean | yes | false | |
1472
1766
 
@@ -1477,13 +1771,13 @@ Props:
1477
1771
  - internal: `false`
1478
1772
  - children: `yes (1..∞)`
1479
1773
  - description: Zoom in/out wrapper transition
1480
- - llmGuidance: Use for energetic cuts. type="zoomIn" feels punchy; type="zoomOut" feels calmer.
1774
+ - llmGuidance: Use for energetic cuts. type controls zoom direction. duration uses theme motion tokens.
1481
1775
 
1482
1776
  Props:
1483
1777
 
1484
1778
  | Prop | Type | Required | Default | Notes |
1485
1779
  | - | - | - | - | - |
1486
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1780
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1487
1781
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1488
1782
  | `type` | enum("zoomIn" \| "zoomOut") | yes | "zoomIn" | |
1489
1783
 
@@ -1496,30 +1790,30 @@ The `examples/` directory contains validated IR JSON files you can use as starti
1496
1790
  Quick validate:
1497
1791
 
1498
1792
  ```bash
1499
- npx waves validate --in examples/basic.v2.json
1793
+ npx waves validate --in examples/basic.v3.json
1500
1794
  ```
1501
1795
 
1502
1796
  Validate all examples:
1503
1797
 
1504
1798
  ```powershell
1505
- Get-ChildItem examples -Filter *.v2.json | ForEach-Object { npx waves validate --in $_.FullName }
1799
+ Get-ChildItem examples -Filter *.v3.json | ForEach-Object { npx waves validate --in $_.FullName }
1506
1800
  ```
1507
1801
 
1508
1802
  Render (writes an MP4; `examples/*.mp4` are gitignored):
1509
1803
 
1510
1804
  ```bash
1511
- npx waves render --in examples/basic.v2.json --out examples/basic.v2.mp4 --codec h264 --crf 28 --concurrency 1
1805
+ npx waves render --in examples/basic.v3.json --out examples/basic.v3.mp4 --codec h264 --crf 28 --concurrency 1
1512
1806
  ```
1513
1807
 
1514
1808
  If an example references `/assets/...`, you must provide those files and pass `--publicDir`:
1515
1809
 
1516
1810
  ```bash
1517
- npx waves render --in examples/intro-stats-outro.v2.json --out examples/intro-stats-outro.v2.mp4 --publicDir ./public
1811
+ npx waves render --in examples/intro-stats-outro.v3.json --out examples/intro-stats-outro.v3.mp4 --publicDir ./public
1518
1812
  ```
1519
1813
 
1520
1814
  ### Example 0: basic starter (segments + composites)
1521
1815
 
1522
- File: `examples/basic.v2.json`
1816
+ File: `examples/basic.v3.json`
1523
1817
 
1524
1818
  This is the default starter IR used by `waves write-ir --template basic`. It demonstrates:
1525
1819
 
@@ -1530,60 +1824,33 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1530
1824
 
1531
1825
  ```json
1532
1826
  {
1533
- "version": "2.0",
1534
- "video": {
1535
- "id": "main",
1536
- "width": 1920,
1537
- "height": 1080,
1538
- "fps": 30,
1539
- "durationInFrames": 165
1540
- },
1827
+ "version": "3.0",
1828
+ "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 168 },
1829
+ "theme": { "preset": "studio-dark" },
1541
1830
  "segments": [
1542
1831
  {
1543
1832
  "id": "scene-1",
1544
1833
  "durationInFrames": 90,
1545
- "transitionToNext": {
1546
- "type": "FadeTransition",
1547
- "durationInFrames": 15
1548
- },
1834
+ "transitionToNext": { "type": "FadeTransition", "duration": "fast" },
1549
1835
  "root": {
1550
1836
  "id": "root",
1551
1837
  "type": "Scene",
1552
- "props": {
1553
- "background": { "type": "color", "value": "#000000" }
1554
- },
1838
+ "props": { "background": { "type": "color", "token": "background" } },
1555
1839
  "children": [
1556
1840
  {
1557
1841
  "id": "title",
1558
1842
  "type": "SplitText",
1559
- "props": {
1560
- "content": "Waves v0.2.0",
1561
- "fontSize": 96,
1562
- "splitBy": "word",
1563
- "stagger": 3,
1564
- "animation": "slideUp"
1565
- }
1843
+ "props": { "content": "Waves v0.5.0", "size": "5xl", "color": "foreground", "font": "display", "weight": "black", "splitBy": "word", "stagger": "fast", "animation": "slideUp" }
1566
1844
  },
1567
1845
  {
1568
1846
  "id": "subtitle",
1569
1847
  "type": "TypewriterText",
1570
- "props": {
1571
- "content": "Composite components + hybrid IR",
1572
- "fontSize": 48,
1573
- "position": "bottom",
1574
- "speed": 1.5
1575
- }
1848
+ "props": { "content": "Unified tokens + presets", "size": "2xl", "color": "mutedForeground", "font": "body", "weight": "semibold", "position": "bottom", "speed": 1.5 }
1576
1849
  },
1577
1850
  {
1578
1851
  "id": "wm",
1579
1852
  "type": "Watermark",
1580
- "props": {
1581
- "type": "text",
1582
- "text": "@depths.ai",
1583
- "position": "bottomRight",
1584
- "opacity": 0.4,
1585
- "size": 60
1586
- }
1853
+ "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": "subtle", "size": "16", "color": "mutedForeground", "textSize": "lg" }
1587
1854
  }
1588
1855
  ]
1589
1856
  }
@@ -1594,25 +1861,11 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1594
1861
  "root": {
1595
1862
  "id": "root-2",
1596
1863
  "type": "Scene",
1597
- "props": {
1598
- "background": { "type": "color", "value": "#0B1220" }
1599
- },
1864
+ "props": { "background": { "type": "color", "token": "surface" } },
1600
1865
  "children": [
1601
- {
1602
- "id": "lower-third",
1603
- "type": "ThirdLowerBanner",
1604
- "props": { "name": "Waves", "title": "v0.2.0 - Composites + transitions", "accentColor": "#3B82F6" }
1605
- },
1606
- {
1607
- "id": "count",
1608
- "type": "AnimatedCounter",
1609
- "props": { "from": 0, "to": 35, "suffix": " components", "fontSize": 96, "color": "#FFFFFF" }
1610
- },
1611
- {
1612
- "id": "wm-2",
1613
- "type": "Watermark",
1614
- "props": { "type": "text", "text": "waves", "position": "topLeft", "opacity": 0.25, "size": 52 }
1615
- }
1866
+ { "id": "lower-third", "type": "ThirdLowerBanner", "props": { "name": "Waves", "title": "v0.5.0 - Robustness & polish", "accent": "primary", "background": "surface" } },
1867
+ { "id": "count", "type": "AnimatedCounter", "props": { "from": 0, "to": 35, "suffix": " components", "size": "5xl", "color": "foreground" } },
1868
+ { "id": "wm-2", "type": "Watermark", "props": { "type": "text", "text": "waves", "position": "topLeft", "opacity": "faint", "size": "12", "color": "mutedForeground", "textSize": "md" } }
1616
1869
  ]
1617
1870
  }
1618
1871
  }
@@ -1622,14 +1875,15 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1622
1875
 
1623
1876
  ### Example 1: primitives-only layout
1624
1877
 
1625
- File: `examples/primitives-card.v2.json`
1878
+ File: `examples/primitives-card.v3.json`
1626
1879
 
1627
1880
  This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1628
1881
 
1629
1882
  ```json
1630
1883
  {
1631
- "version": "2.0",
1884
+ "version": "3.0",
1632
1885
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 150 },
1886
+ "theme": { "preset": "studio-dark" },
1633
1887
  "segments": [
1634
1888
  {
1635
1889
  "id": "card",
@@ -1637,49 +1891,49 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1637
1891
  "root": {
1638
1892
  "id": "scene",
1639
1893
  "type": "Scene",
1640
- "props": { "background": { "type": "color", "value": "#0B1220" } },
1894
+ "props": { "background": { "type": "color", "token": "surface" } },
1641
1895
  "children": [
1642
- { "id": "accent", "type": "Shape", "props": { "shape": "rect", "x": 180, "y": 200, "width": 12, "height": 680, "fill": "#3B82F6", "opacity": 1 } },
1896
+ { "id": "accent", "type": "Shape", "props": { "shape": "rect", "x": 180, "y": 200, "width": 12, "height": 680, "fill": "primary", "opacity": 1 } },
1643
1897
  {
1644
1898
  "id": "panel",
1645
- "type": "Box",
1646
- "props": { "x": 192, "y": 200, "width": 1548, "height": 680, "padding": 0, "backgroundColor": "rgba(255,255,255,0.06)", "borderRadius": 36, "opacity": 1 },
1899
+ "type": "Frame",
1900
+ "props": { "x": 192, "y": 200, "width": 1548, "height": 680, "padding": "0", "backgroundColor": "surface", "borderRadius": "lg", "opacity": 0.7 },
1647
1901
  "children": [
1648
1902
  {
1649
1903
  "id": "panel-grid",
1650
1904
  "type": "Grid",
1651
- "props": { "columns": 2, "rows": 2, "gap": 28, "padding": 64, "align": "stretch", "justify": "stretch" },
1905
+ "props": { "columns": 2, "rows": 2, "gap": "8", "padding": "16", "align": "stretch", "justify": "stretch" },
1652
1906
  "children": [
1653
1907
  {
1654
1908
  "id": "tile-1",
1655
1909
  "type": "Box",
1656
- "props": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1910
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1657
1911
  "children": [
1658
- { "id": "t1", "type": "Text", "props": { "content": "Primitives", "fontSize": 64, "position": "center", "animation": "fade" } }
1912
+ { "id": "t1", "type": "Text", "props": { "content": "Primitives", "size": "3xl", "position": "center", "animation": "fade" } }
1659
1913
  ]
1660
1914
  },
1661
1915
  {
1662
1916
  "id": "tile-2",
1663
1917
  "type": "Box",
1664
- "props": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1918
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1665
1919
  "children": [
1666
- { "id": "t2", "type": "Text", "props": { "content": "Box + Grid", "fontSize": 54, "position": "center", "animation": "slide" } }
1920
+ { "id": "t2", "type": "Text", "props": { "content": "Box + Grid", "size": "2xl", "position": "center", "animation": "slide" } }
1667
1921
  ]
1668
1922
  },
1669
1923
  {
1670
1924
  "id": "tile-3",
1671
1925
  "type": "Box",
1672
- "props": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1926
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1673
1927
  "children": [
1674
- { "id": "t3", "type": "Text", "props": { "content": "Shape", "fontSize": 54, "position": "center", "animation": "zoom" } }
1928
+ { "id": "t3", "type": "Text", "props": { "content": "Shape", "size": "2xl", "position": "center", "animation": "zoom" } }
1675
1929
  ]
1676
1930
  },
1677
1931
  {
1678
1932
  "id": "tile-4",
1679
1933
  "type": "Box",
1680
- "props": { "x": 0, "y": 0, "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1934
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1681
1935
  "children": [
1682
- { "id": "t4", "type": "Text", "props": { "content": "Text", "fontSize": 54, "position": "center", "animation": "fade" } }
1936
+ { "id": "t4", "type": "Text", "props": { "content": "Text", "size": "2xl", "position": "center", "animation": "fade" } }
1683
1937
  ]
1684
1938
  }
1685
1939
  ]
@@ -1691,10 +1945,12 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1691
1945
  "type": "Text",
1692
1946
  "props": {
1693
1947
  "content": "This entire layout is built from primitives only.",
1694
- "fontSize": 34,
1695
1948
  "position": "bottom",
1696
1949
  "animation": "fade",
1697
- "color": "#C7D2FE"
1950
+ "size": "xl",
1951
+ "color": "mutedForeground",
1952
+ "font": "body",
1953
+ "weight": "medium"
1698
1954
  }
1699
1955
  }
1700
1956
  ]
@@ -1708,64 +1964,65 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1708
1964
 
1709
1965
  When authoring segments with overlaps, the total end time is:
1710
1966
 
1711
- `sum(segments[i].durationInFrames) - sum(segments[i].transitionToNext.durationInFrames)`
1967
+ `sum(segments[i].durationInFrames) - sum(theme.motion.durationsInFrames[segments[i].transitionToNext.duration])`
1712
1968
 
1713
1969
  Example 2 has 4 segments:
1714
1970
 
1715
1971
  - segment durations: 90 + 90 + 90 + 90 = 360
1716
- - overlap durations: 15 + 15 + 15 = 45
1717
- - video duration: 360 - 45 = 315 (must match `video.durationInFrames`)
1972
+ - overlap durations: 24 + 24 + 24 = 72 (for the `studio-dark` preset where `"base"` = 24 frames)
1973
+ - video duration: 360 - 72 = 288 (must match `video.durationInFrames`)
1718
1974
 
1719
1975
  ### Example 2: segment transitions showcase
1720
1976
 
1721
- File: `examples/transitions-showcase.v2.json`
1977
+ File: `examples/transitions-showcase.v3.json`
1722
1978
 
1723
1979
  This example shows how to chain segments with overlaps using `transitionToNext`.
1724
1980
 
1725
1981
  ```json
1726
1982
  {
1727
- "version": "2.0",
1728
- "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 315 },
1983
+ "version": "3.0",
1984
+ "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 288 },
1985
+ "theme": { "preset": "studio-dark" },
1729
1986
  "segments": [
1730
1987
  {
1731
1988
  "id": "s1",
1732
1989
  "durationInFrames": 90,
1733
- "transitionToNext": { "type": "FadeTransition", "durationInFrames": 15 },
1990
+ "transitionToNext": { "type": "FadeTransition", "duration": "base" },
1734
1991
  "root": {
1735
1992
  "id": "scene-1",
1736
1993
  "type": "Scene",
1737
- "props": { "background": { "type": "color", "value": "#000000" } },
1994
+ "props": { "background": { "type": "color", "token": "background" } },
1738
1995
  "children": [
1739
- { "id": "title-1", "type": "SplitText", "props": { "content": "FadeTransition", "fontSize": 100, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "center" } },
1740
- { "id": "wm-1", "type": "Watermark", "props": { "type": "text", "text": "waves", "position": "bottomRight", "opacity": 0.35, "size": 60 } }
1996
+ { "id": "title-1", "type": "SplitText", "props": { "content": "FadeTransition", "size": "6xl", "splitBy": "word", "stagger": "fast", "animation": "slideUp", "position": "center" } },
1997
+ { "id": "wm-1", "type": "Watermark", "props": { "type": "text", "text": "waves", "position": "bottomRight", "opacity": "subtle", "color": "mutedForeground", "textSize": "lg" } }
1741
1998
  ]
1742
1999
  }
1743
2000
  },
1744
2001
  {
1745
2002
  "id": "s2",
1746
2003
  "durationInFrames": 90,
1747
- "transitionToNext": { "type": "SlideTransition", "durationInFrames": 15, "props": { "direction": "left", "distance": 120 } },
2004
+ "transitionToNext": { "type": "SlideTransition", "duration": "base", "props": { "direction": "left", "distance": "travel" } },
1748
2005
  "root": {
1749
2006
  "id": "scene-2",
1750
2007
  "type": "Scene",
1751
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2008
+ "props": { "background": { "type": "color", "token": "surface" } },
1752
2009
  "children": [
1753
- { "id": "title-2", "type": "SplitText", "props": { "content": "SlideTransition", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "center" } },
1754
- { "id": "subtitle-2", "type": "TypewriterText", "props": { "content": "direction=left, distance=120", "fontSize": 44, "position": "bottom", "speed": 1.6, "showCursor": true } }
2010
+ { "id": "title-2", "type": "SplitText", "props": { "content": "SlideTransition", "size": "6xl", "splitBy": "word", "stagger": "fast", "animation": "slideUp", "position": "center" } },
2011
+ { "id": "subtitle-2", "type": "TypewriterText", "props": { "content": "direction=left, distance=travel", "size": "xl", "position": "bottom", "speed": 1.6, "showCursor": true } }
1755
2012
  ]
1756
2013
  }
1757
2014
  },
1758
2015
  {
1759
2016
  "id": "s3",
1760
2017
  "durationInFrames": 90,
1761
- "transitionToNext": { "type": "WipeTransition", "durationInFrames": 15, "props": { "direction": "diagonal", "softEdge": true } },
2018
+ "transitionToNext": { "type": "WipeTransition", "duration": "base", "props": { "direction": "diagonal", "softEdge": true } },
1762
2019
  "root": {
1763
2020
  "id": "scene-3",
1764
2021
  "type": "Scene",
1765
- "props": { "background": { "type": "color", "value": "#111827" } },
2022
+ "props": { "background": { "type": "color", "token": "muted" } },
1766
2023
  "children": [
1767
- { "id": "title-3", "type": "SplitText", "props": { "content": "WipeTransition", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "scale", "position": "center" } },
1768
- { "id": "subtitle-3", "type": "TypewriterText", "props": { "content": "direction=diagonal, softEdge=true", "fontSize": 44, "position": "bottom", "speed": 1.6, "showCursor": false } }
2024
+ { "id": "title-3", "type": "SplitText", "props": { "content": "WipeTransition", "size": "6xl", "splitBy": "word", "stagger": "fast", "animation": "scale", "position": "center" } },
2025
+ { "id": "subtitle-3", "type": "TypewriterText", "props": { "content": "direction=diagonal, softEdge=true", "size": "xl", "position": "bottom", "speed": 1.6, "showCursor": false } }
1769
2026
  ]
1770
2027
  }
1771
2028
  },
@@ -1775,10 +2032,10 @@ This example shows how to chain segments with overlaps using `transitionToNext`.
1775
2032
  "root": {
1776
2033
  "id": "scene-4",
1777
2034
  "type": "Scene",
1778
- "props": { "background": { "type": "color", "value": "#000000" } },
2035
+ "props": { "background": { "type": "color", "token": "background" } },
1779
2036
  "children": [
1780
- { "id": "title-4", "type": "SplitText", "props": { "content": "Done", "fontSize": 120, "splitBy": "letter", "stagger": 2, "animation": "rotate", "position": "center" } },
1781
- { "id": "wm-4", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": 0.4, "size": 60 } }
2037
+ { "id": "title-4", "type": "SplitText", "props": { "content": "Done", "size": "6xl", "splitBy": "letter", "stagger": "fast", "animation": "rotate", "position": "center" } },
2038
+ { "id": "wm-4", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": "subtle", "color": "mutedForeground", "textSize": "lg" } }
1782
2039
  ]
1783
2040
  }
1784
2041
  }
@@ -1788,7 +2045,7 @@ This example shows how to chain segments with overlaps using `transitionToNext`.
1788
2045
 
1789
2046
  ### Example 3: data dashboard
1790
2047
 
1791
- File: `examples/data-dashboard.v2.json`
2048
+ File: `examples/data-dashboard.v3.json`
1792
2049
 
1793
2050
  This example uses data composites inside a `Grid`:
1794
2051
 
@@ -1799,8 +2056,9 @@ This example uses data composites inside a `Grid`:
1799
2056
 
1800
2057
  ```json
1801
2058
  {
1802
- "version": "2.0",
2059
+ "version": "3.0",
1803
2060
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 180 },
2061
+ "theme": { "preset": "studio-dark" },
1804
2062
  "segments": [
1805
2063
  {
1806
2064
  "id": "dashboard",
@@ -1808,14 +2066,14 @@ This example uses data composites inside a `Grid`:
1808
2066
  "root": {
1809
2067
  "id": "scene",
1810
2068
  "type": "Scene",
1811
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2069
+ "props": { "background": { "type": "color", "token": "surface" } },
1812
2070
  "children": [
1813
2071
  {
1814
2072
  "id": "grid",
1815
2073
  "type": "Grid",
1816
- "props": { "columns": 2, "rows": 2, "gap": 36, "padding": 90, "align": "stretch", "justify": "stretch" },
2074
+ "props": { "columns": 2, "rows": 2, "gap": "8", "padding": "24", "align": "stretch", "justify": "stretch" },
1817
2075
  "children": [
1818
- { "id": "ring", "type": "ProgressRing", "props": { "percentage": 72, "size": 260, "color": "#22C55E", "showLabel": true } },
2076
+ { "id": "ring", "type": "ProgressRing", "props": { "percentage": 72, "size": "xl", "color": "secondary", "showLabel": true } },
1819
2077
  {
1820
2078
  "id": "bars",
1821
2079
  "type": "BarChart",
@@ -1824,10 +2082,10 @@ This example uses data composites inside a `Grid`:
1824
2082
  "showValues": true,
1825
2083
  "showGrid": true,
1826
2084
  "data": [
1827
- { "label": "A", "value": 32, "color": "#3B82F6" },
1828
- { "label": "B", "value": 56, "color": "#22C55E" },
1829
- { "label": "C", "value": 18, "color": "#F59E0B" },
1830
- { "label": "D", "value": 44, "color": "#EF4444" }
2085
+ { "label": "A", "value": 32, "color": "primary" },
2086
+ { "label": "B", "value": 56, "color": "secondary" },
2087
+ { "label": "C", "value": 18, "color": "accent" },
2088
+ { "label": "D", "value": 44, "color": "ring" }
1831
2089
  ]
1832
2090
  }
1833
2091
  },
@@ -1835,8 +2093,8 @@ This example uses data composites inside a `Grid`:
1835
2093
  "id": "line",
1836
2094
  "type": "LineGraph",
1837
2095
  "props": {
1838
- "color": "#60A5FA",
1839
- "strokeWidth": 4,
2096
+ "color": "primary",
2097
+ "strokeWidth": "2",
1840
2098
  "showDots": true,
1841
2099
  "fillArea": true,
1842
2100
  "animate": "draw",
@@ -1850,10 +2108,10 @@ This example uses data composites inside a `Grid`:
1850
2108
  ]
1851
2109
  }
1852
2110
  },
1853
- { "id": "progress", "type": "ProgressBar", "props": { "label": "Rendering", "position": "bottom", "height": 16, "color": "#A855F7", "showPercentage": true } }
2111
+ { "id": "progress", "type": "ProgressBar", "props": { "label": "Rendering", "position": "bottom", "height": "4", "barColor": "accent", "showPercentage": true } }
1854
2112
  ]
1855
2113
  },
1856
- { "id": "title", "type": "SplitText", "props": { "content": "Data composites", "fontSize": 86, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "top" } }
2114
+ { "id": "title", "type": "SplitText", "props": { "content": "Data composites", "size": "5xl", "splitBy": "word", "stagger": "fast", "animation": "slideUp", "position": "top" } }
1857
2115
  ]
1858
2116
  }
1859
2117
  }
@@ -1863,7 +2121,7 @@ This example uses data composites inside a `Grid`:
1863
2121
 
1864
2122
  ### Example 4: vertical social-style video (9:16)
1865
2123
 
1866
- File: `examples/social-vertical.v2.json`
2124
+ File: `examples/social-vertical.v3.json`
1867
2125
 
1868
2126
  This example uses:
1869
2127
 
@@ -1873,19 +2131,20 @@ This example uses:
1873
2131
 
1874
2132
  ```json
1875
2133
  {
1876
- "version": "2.0",
1877
- "video": { "id": "main", "width": 1080, "height": 1920, "fps": 30, "durationInFrames": 225 },
2134
+ "version": "3.0",
2135
+ "video": { "id": "main", "width": 1080, "height": 1920, "fps": 30, "durationInFrames": 216 },
2136
+ "theme": { "preset": "studio-dark" },
1878
2137
  "segments": [
1879
2138
  {
1880
2139
  "id": "ig",
1881
2140
  "durationInFrames": 120,
1882
- "transitionToNext": { "type": "CircularReveal", "durationInFrames": 15, "props": { "direction": "open", "center": { "x": 0.5, "y": 0.45 } } },
2141
+ "transitionToNext": { "type": "CircularReveal", "duration": "base", "props": { "direction": "open", "center": { "x": 0.5, "y": 0.45 } } },
1883
2142
  "root": {
1884
2143
  "id": "ig-root",
1885
2144
  "type": "Scene",
1886
- "props": { "background": { "type": "color", "value": "#000000" } },
2145
+ "props": { "background": { "type": "color", "token": "background" } },
1887
2146
  "children": [
1888
- { "id": "ig", "type": "InstagramStory", "props": { "backgroundColor": "#0B1220", "username": "depths.ai", "text": "Waves v0.2.0 ships composites + hybrid IR", "sticker": "poll" } }
2147
+ { "id": "ig", "type": "InstagramStory", "props": { "background": "background", "username": "depths.ai", "text": "Waves v0.5.0 improves robustness + performance", "sticker": "poll" } }
1889
2148
  ]
1890
2149
  }
1891
2150
  },
@@ -1895,10 +2154,10 @@ This example uses:
1895
2154
  "root": {
1896
2155
  "id": "tt-root",
1897
2156
  "type": "Scene",
1898
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2157
+ "props": { "background": { "type": "color", "token": "surface" } },
1899
2158
  "children": [
1900
- { "id": "caption", "type": "TikTokCaption", "props": { "text": "Composites make LLM video authoring dramatically easier", "position": "bottom", "highlightStyle": "bounce", "fontSize": 58, "strokeWidth": 4 } },
1901
- { "id": "wm", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "topRight", "opacity": 0.35, "size": 72 } }
2159
+ { "id": "caption", "type": "TikTokCaption", "props": { "text": "Token-only styling makes LLM video authoring dramatically easier", "position": "bottom", "highlightStyle": "bounce", "size": "4xl", "strokeWidth": "2" } },
2160
+ { "id": "wm", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "topRight", "opacity": "subtle", "color": "mutedForeground", "textSize": "lg" } }
1902
2161
  ]
1903
2162
  }
1904
2163
  }
@@ -1908,7 +2167,7 @@ This example uses:
1908
2167
 
1909
2168
  ### Example 5: intro -> stats -> outro (assets)
1910
2169
 
1911
- File: `examples/intro-stats-outro.v2.json`
2170
+ File: `examples/intro-stats-outro.v3.json`
1912
2171
 
1913
2172
  This example uses branding + layout + data composites:
1914
2173
 
@@ -1920,34 +2179,35 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
1920
2179
 
1921
2180
  ```json
1922
2181
  {
1923
- "version": "2.0",
1924
- "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 360 },
2182
+ "version": "3.0",
2183
+ "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 342 },
2184
+ "theme": { "preset": "studio-dark" },
1925
2185
  "segments": [
1926
2186
  {
1927
2187
  "id": "intro",
1928
2188
  "durationInFrames": 120,
1929
- "transitionToNext": { "type": "FadeTransition", "durationInFrames": 15 },
2189
+ "transitionToNext": { "type": "FadeTransition", "duration": "base" },
1930
2190
  "root": {
1931
2191
  "id": "intro-scene",
1932
2192
  "type": "Scene",
1933
- "props": { "background": { "type": "color", "value": "#000000" } },
2193
+ "props": { "background": { "type": "color", "token": "background" } },
1934
2194
  "children": [
1935
- { "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.2.0", "backgroundColor": "#000000", "primaryColor": "#FFFFFF" } }
2195
+ { "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.5.0" } }
1936
2196
  ]
1937
2197
  }
1938
2198
  },
1939
2199
  {
1940
2200
  "id": "stats",
1941
2201
  "durationInFrames": 150,
1942
- "transitionToNext": { "type": "SlideTransition", "durationInFrames": 15, "props": { "direction": "up", "distance": 140 } },
2202
+ "transitionToNext": { "type": "SlideTransition", "duration": "base", "props": { "direction": "up", "distance": "travel" } },
1943
2203
  "root": {
1944
2204
  "id": "stats-scene",
1945
2205
  "type": "Scene",
1946
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2206
+ "props": { "background": { "type": "color", "token": "surface" } },
1947
2207
  "children": [
1948
- { "id": "lower-third", "type": "ThirdLowerBanner", "props": { "name": "Waves", "title": "Composite components", "accentColor": "#22C55E" } },
1949
- { "id": "counter", "type": "AnimatedCounter", "props": { "from": 0, "to": 44, "suffix": " types", "fontSize": 110, "color": "#FFFFFF", "animationType": "spring" } },
1950
- { "id": "bars", "type": "BarChart", "props": { "orientation": "horizontal", "showValues": true, "showGrid": false, "data": [ { "label": "Primitives", "value": 10, "color": "#3B82F6" }, { "label": "Composites", "value": 34, "color": "#22C55E" } ] } }
2208
+ { "id": "lower-third", "type": "ThirdLowerBanner", "props": { "name": "Waves", "title": "Composite components", "accent": "secondary" } },
2209
+ { "id": "counter", "type": "AnimatedCounter", "props": { "from": 0, "to": 48, "suffix": " types", "size": "6xl", "color": "foreground", "animationType": "spring" } },
2210
+ { "id": "bars", "type": "BarChart", "props": { "orientation": "horizontal", "showValues": true, "showGrid": false, "data": [ { "label": "Primitives", "value": 12, "color": "primary" }, { "label": "Composites", "value": 36, "color": "secondary" } ] } }
1951
2211
  ]
1952
2212
  }
1953
2213
  },
@@ -1957,7 +2217,7 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
1957
2217
  "root": {
1958
2218
  "id": "outro-scene",
1959
2219
  "type": "Scene",
1960
- "props": { "background": { "type": "color", "value": "#000000" } },
2220
+ "props": { "background": { "type": "color", "token": "background" } },
1961
2221
  "children": [
1962
2222
  {
1963
2223
  "id": "outro",
@@ -1965,18 +2225,11 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
1965
2225
  "props": {
1966
2226
  "logoSrc": "/assets/logo.svg",
1967
2227
  "message": "Thanks for watching",
1968
- "backgroundColor": "#000000",
1969
- "ctaButtons": [
1970
- { "text": "Star", "icon": "*" },
1971
- { "text": "Follow", "icon": "->" }
1972
- ],
1973
- "socialHandles": [
1974
- { "platform": "twitter", "handle": "@depths_ai" },
1975
- { "platform": "youtube", "handle": "@depths-ai" }
1976
- ]
2228
+ "ctaButtons": [ { "text": "Star", "icon": "*" }, { "text": "Follow", "icon": "->" } ],
2229
+ "socialHandles": [ { "platform": "twitter", "handle": "@depths_ai" }, { "platform": "youtube", "handle": "@depths-ai" } ]
1977
2230
  }
1978
2231
  },
1979
- { "id": "wm", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": 0.25, "size": 64 } }
2232
+ { "id": "wm", "type": "Watermark", "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": "faint", "color": "mutedForeground", "textSize": "lg" } }
1980
2233
  ]
1981
2234
  }
1982
2235
  }
@@ -1986,7 +2239,7 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
1986
2239
 
1987
2240
  ### Example 6: timeline mode (global audio)
1988
2241
 
1989
- File: `examples/timeline-global-audio.v2.json`
2242
+ File: `examples/timeline-global-audio.v3.json`
1990
2243
 
1991
2244
  This example uses `timeline[]` to place a global `Audio` track as a root node spanning the entire video.
1992
2245
 
@@ -1994,17 +2247,18 @@ This is a common reason to choose timeline mode over segments mode: you want "gl
1994
2247
 
1995
2248
  ```json
1996
2249
  {
1997
- "version": "2.0",
2250
+ "version": "3.0",
1998
2251
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 150 },
2252
+ "theme": { "preset": "studio-dark" },
1999
2253
  "timeline": [
2000
2254
  {
2001
2255
  "id": "scene",
2002
2256
  "type": "Scene",
2003
2257
  "timing": { "from": 0, "durationInFrames": 150 },
2004
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2258
+ "props": { "background": { "type": "color", "token": "surface" } },
2005
2259
  "children": [
2006
- { "id": "title", "type": "SplitText", "props": { "content": "Timeline mode", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "top" } },
2007
- { "id": "subtitle", "type": "TypewriterText", "props": { "content": "Audio spans the whole video", "fontSize": 44, "position": "bottom", "speed": 1.4, "showCursor": false } }
2260
+ { "id": "title", "type": "SplitText", "props": { "content": "Timeline mode", "size": "6xl", "splitBy": "word", "stagger": "fast", "animation": "slideUp", "position": "top" } },
2261
+ { "id": "subtitle", "type": "TypewriterText", "props": { "content": "Audio spans the whole video", "size": "xl", "position": "bottom", "speed": 1.4, "showCursor": false } }
2008
2262
  ]
2009
2263
  },
2010
2264
  {
@@ -2024,8 +2278,9 @@ import { renderVideo } from '@depths/waves';
2024
2278
 
2025
2279
  await renderVideo(
2026
2280
  {
2027
- version: '2.0',
2281
+ version: '3.0',
2028
2282
  video: { id: 'main', width: 1920, height: 1080, fps: 30, durationInFrames: 60 },
2283
+ theme: { preset: 'studio-dark' },
2029
2284
  segments: [
2030
2285
  {
2031
2286
  id: 'scene-1',
@@ -2033,7 +2288,7 @@ await renderVideo(
2033
2288
  root: {
2034
2289
  id: 'root',
2035
2290
  type: 'Scene',
2036
- props: { background: { type: 'color', value: '#000000' } },
2291
+ props: { background: { type: 'color', token: 'background' } },
2037
2292
  children: [{ id: 't1', type: 'Text', props: { content: 'Hello' } }]
2038
2293
  }
2039
2294
  }
@@ -2043,7 +2298,7 @@ await renderVideo(
2043
2298
  );
2044
2299
  ```
2045
2300
 
2046
- ## Assets and paths (Windows/Linux)
2301
+ ## Assets and paths (Windows/Linux)
2047
2302
 
2048
2303
  In IR JSON, asset paths must be either:
2049
2304
 
@@ -2052,12 +2307,33 @@ In IR JSON, asset paths must be either:
2052
2307
 
2053
2308
  On Windows:
2054
2309
 
2055
- - Prefer forward slashes in IR (`/assets/foo.png`), not `C:\\...` paths.
2056
- - If you pass Windows paths to the CLI (e.g. `--out`), quote them if they contain spaces.
2057
-
2058
- ## Rendering prerequisites
2059
-
2060
- Waves uses `@remotion/renderer` (Remotion 4.x). Remotion runs headless Chromium; if a compatible browser isn't available, it may download one automatically (or you can configure browser paths via Remotion). Remotion 4 also ships its own ffmpeg binary, so you typically do not need `ffmpeg` installed on your PATH.
2310
+ - Prefer forward slashes in IR (`/assets/foo.png`), not `C:\\...` paths.
2311
+ - If you pass Windows paths to the CLI (e.g. `--out`), quote them if they contain spaces.
2312
+
2313
+ ## Robustness / Troubleshooting
2314
+
2315
+ ### Missing local assets
2316
+
2317
+ If your IR contains local `/assets/...` references, you **must** pass `--publicDir` (CLI) / `publicDir` (library). Waves checks that referenced files exist under `<publicDir>/assets/...` before bundling/rendering and fails fast with a structured error listing missing assets.
2318
+
2319
+ ### Theme font loading failures
2320
+
2321
+ Theme fonts are loaded via `FontFace` at render time. If any font fails to load, Waves cancels the render with a clear error. Use either:
2322
+
2323
+ - a local `/assets/...` font with `--publicDir`, or
2324
+ - a full `http(s)://...` URL.
2325
+
2326
+ ### Segment duration mismatches
2327
+
2328
+ In segments mode, the expected total duration is:
2329
+
2330
+ `sum(segment.durationInFrames) - sum(overlapFrames)`
2331
+
2332
+ Where each overlap is derived from the `segment.transitionToNext.duration` token resolved from the theme (`theme.motion.durationsInFrames`). If you see `DURATION_MISMATCH`, set `video.durationInFrames` to the expected duration (the validator error message includes a breakdown).
2333
+
2334
+ ## Rendering prerequisites
2335
+
2336
+ Waves uses `@remotion/renderer` (Remotion 4.x). Remotion runs headless Chromium; if a compatible browser isn't available, it may download one automatically (or you can configure browser paths via Remotion). Remotion 4 also ships its own ffmpeg binary, so you typically do not need `ffmpeg` installed on your PATH.
2061
2337
 
2062
2338
  ## Local CLI testing (before publishing)
2063
2339
 
@@ -2067,9 +2343,9 @@ From the `waves/` package directory:
2067
2343
  npm install
2068
2344
  npm run build
2069
2345
  node dist/cli.js --help
2070
- node dist/cli.js write-ir --template basic --pretty --out examples/basic.v2.json
2071
- node dist/cli.js validate --in examples/basic.v2.json
2072
- node dist/cli.js render --in examples/basic.v2.json --out examples/basic.v2.mp4 --codec h264 --crf 28 --concurrency 1
2346
+ node dist/cli.js write-ir --template basic --pretty --out examples/basic.v3.json
2347
+ node dist/cli.js validate --in examples/basic.v3.json
2348
+ node dist/cli.js render --in examples/basic.v3.json --out examples/basic.v3.mp4 --codec h264 --crf 28 --concurrency 1
2073
2349
  ```
2074
2350
 
2075
2351
  To test the installed experience:
@@ -2101,7 +2377,7 @@ The build emits ESM into `dist/`. Most CLI tests execute the built CLI via `node
2101
2377
 
2102
2378
  - `src/cli.ts`: the CLI (`waves <command> ...`)
2103
2379
  - `src/llm/prompt.ts`: system prompt + prompt payload (schemas + catalog)
2104
- - `src/ir/schema.ts`: IR v2.0 Zod schemas (authoring + full IR)
2380
+ - `src/ir/schema.ts`: IR v3.0 Zod schemas (authoring + full IR)
2105
2381
  - `src/ir/migrations.ts`: compiler (`compileToRenderGraph`) from authored IR -> render timeline
2106
2382
  - `src/core/registry.ts`: component registry + JSON Schema export for LLM use
2107
2383
  - `src/core/validator.ts`: schema + semantics + registry validation