@depths/waves 0.3.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.3.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
 
@@ -13,12 +13,19 @@ v0.2.0 introduced a shadcn-like catalog of higher-level "composite" components a
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.3.0 focuses on **seamless alignment + overlays**:
17
-
18
- - **Layout-safe primitives:** components that use normal layout flow by default (no accidental `position: absolute`)
19
- - **Explicit layering primitives:** `Layers` + `Layer` for deterministic overlays (z-index, inset, opacity)
20
- - **Explicit positioning primitive:** `Frame` for deliberate `x/y/width/height` placement (breaking change: `Box` no longer supports `x/y`)
21
- - **Visual debugging workflow:** `waves stills` and `--debugBounds/--debugLabels` to inspect alignment issues without pixel tests
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)
22
29
 
23
30
  ## Table of contents
24
31
 
@@ -31,7 +38,7 @@ v0.3.0 focuses on **seamless alignment + overlays**:
31
38
  - [Rendering model](#rendering-model)
32
39
  - [Installation](#installation)
33
40
  - [CLI (agent workflow)](#cli-agent-workflow)
34
- - [IR v2.0 (authoring contract)](#ir-v20-authoring-contract)
41
+ - [IR v3.0 (authoring contract)](#ir-v30-authoring-contract)
35
42
  - [Components (primitives + composites)](#components-primitives--composites)
36
43
  - [Examples (composition recipes)](#examples-composition-recipes)
37
44
  - [Library API (quickstart)](#library-api-quickstart)
@@ -69,7 +76,7 @@ At render time, Waves is "just React + Remotion". The strictness lives earlier (
69
76
  The codebase has a small number of core concepts:
70
77
 
71
78
  - **Video IR (`VideoIRSchema`)**: the JSON document format you author.
72
- - v0.3.0 targets `version: "2.0"` only.
79
+ - v0.4.0+ targets `version: "3.0"` only.
73
80
  - You author exactly one of `segments[]` (recommended) or `timeline[]` (escape hatch).
74
81
  - **Component Registry (`ComponentRegistry`)**: a map from string `type` -> React component + Zod props schema + metadata.
75
82
  - Only registered components can render.
@@ -106,7 +113,7 @@ Why the internal `Segment` wrapper exists:
106
113
  Overlap math:
107
114
 
108
115
  - Let segment `i` have duration `Di`.
109
- - 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).
110
117
  - Then the compiled start time is:
111
118
  - `start(0) = 0`
112
119
  - `start(i+1) = start(i) + Di - Oi`
@@ -185,9 +192,9 @@ For an agent that can execute terminal commands, the recommended flow is:
185
192
  1. `waves prompt --format json --out waves-prompt.json`
186
193
  2. Use `waves-prompt.json.systemPrompt` + `waves-prompt.json.schemas` + `waves-prompt.json.catalog` as the authoring contract for the model
187
194
  3. Have the model output a single JSON object (Video IR)
188
- 4. Write that JSON to `video.v2.json`
189
- 5. `waves validate --in video.v2.json` until it passes
190
- 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 ...`
191
198
 
192
199
  ### 1) Get the prompt payload (system prompt + schemas + catalog)
193
200
 
@@ -221,7 +228,7 @@ If you register custom components at module import time, include them with repea
221
228
  ```bash
222
229
  npx waves prompt --format json --register ./src/register-waves-components.ts
223
230
  npx waves schema --kind components --register ./src/register-waves-components.ts
224
- 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
225
232
  ```
226
233
 
227
234
  Important notes on `--register`:
@@ -234,31 +241,31 @@ Important notes on `--register`:
234
241
  ### 2) Write IR JSON
235
242
 
236
243
  ```bash
237
- npx waves write-ir --template basic --pretty --out ./video.v2.json
244
+ npx waves write-ir --template basic --pretty --out ./video.v3.json
238
245
  ```
239
246
 
240
247
  ### 3) Validate IR JSON
241
248
 
242
249
  ```bash
243
- npx waves validate --in ./video.v2.json
250
+ npx waves validate --in ./video.v3.json
244
251
  ```
245
252
 
246
253
  Structured validation result:
247
254
 
248
255
  ```bash
249
- npx waves validate --in ./video.v2.json --format json
256
+ npx waves validate --in ./video.v3.json --format json
250
257
  ```
251
258
 
252
259
  ### 4) Render MP4
253
260
 
254
261
  ```bash
255
- 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
256
263
  ```
257
264
 
258
265
  If your IR references `"/assets/..."` paths, pass `--publicDir` and ensure the files exist at `${publicDir}/assets/...`:
259
266
 
260
267
  ```bash
261
- 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
262
269
  ```
263
270
 
264
271
  ### Exit codes
@@ -280,7 +287,7 @@ Prints a short help text.
280
287
 
281
288
  #### `waves --version`
282
289
 
283
- Prints the package version (e.g. `0.2.0`).
290
+ Prints the package version (e.g. `0.5.0`).
284
291
 
285
292
  #### `waves prompt`
286
293
 
@@ -343,21 +350,42 @@ npx waves catalog
343
350
  npx waves catalog --format json --pretty --out ./waves-catalog.json
344
351
  ```
345
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
+
346
372
  #### `waves write-ir`
347
373
 
348
- 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).
349
375
 
350
376
  Flags:
351
377
 
352
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)
353
381
  - `--pretty`
354
382
  - `--out <path>` (required)
355
383
 
356
384
  Examples:
357
385
 
358
386
  ```bash
359
- npx waves write-ir --template minimal --pretty --out ./video.v2.json
360
- 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
361
389
  ```
362
390
 
363
391
  #### `waves validate`
@@ -369,6 +397,8 @@ Flags:
369
397
  - `--in <path>` (required)
370
398
  - `--format text|json` (default `text`)
371
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)
372
402
  - `--register <module>` (repeatable)
373
403
 
374
404
  Behavior:
@@ -379,8 +409,8 @@ Behavior:
379
409
  Examples:
380
410
 
381
411
  ```bash
382
- npx waves validate --in ./video.v2.json
383
- 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
384
414
  ```
385
415
 
386
416
  #### `waves render`
@@ -395,6 +425,8 @@ Flags:
395
425
  - `--codec h264|h265|vp8|vp9` (optional; default `h264`)
396
426
  - `--crf <n>` (optional; forwarded to Remotion renderer)
397
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)
398
430
  - `--debugBounds` (optional; draws debug outlines for every rendered node)
399
431
  - `--debugLabels` (optional; labels debug outlines with `type#id`)
400
432
  - `--register <module>` (repeatable)
@@ -403,9 +435,9 @@ Flags:
403
435
  Examples:
404
436
 
405
437
  ```bash
406
- npx waves render --in ./video.v2.json --out ./output.mp4 --codec h264 --crf 28 --concurrency 1
407
- npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public
408
- npx waves render --in ./video.v2.json --out ./output.mp4 --publicDir ./public --debugBounds --debugLabels
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
409
441
  ```
410
442
 
411
443
  #### `waves stills`
@@ -421,19 +453,21 @@ Flags:
421
453
  - `--imageFormat png|jpeg|webp` (optional; default `png`)
422
454
  - `--scale <n>` (optional; default `1`)
423
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)
424
458
  - `--debugBounds` / `--debugLabels` (same as `waves render`)
425
459
  - `--register <module>` (repeatable)
426
460
 
427
461
  Examples:
428
462
 
429
463
  ```bash
430
- npx waves stills --in ./video.v2.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public
431
- npx waves stills --in ./video.v2.json --outDir ./examples/_stills --frames "0,45,90" --publicDir ./public --debugBounds --debugLabels
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
432
466
  ```
433
467
 
434
- ## IR v2.0 (authoring contract)
468
+ ## IR v3.0 (authoring contract)
435
469
 
436
- v0.3.0 targets `version: "2.0"` only.
470
+ v0.4.0+ targets `version: "3.0"` only.
437
471
 
438
472
  ### Recommended: `segments[]` (high-level)
439
473
 
@@ -441,18 +475,19 @@ In segments mode, you provide sequential segments and Waves compiles them into a
441
475
 
442
476
  Key rules:
443
477
 
444
- - Segment overlap is controlled by `transitionToNext.durationInFrames`.
478
+ - Segment overlap is controlled by `transitionToNext.duration` (a theme motion token key like `"fast"`, `"base"`, `"slow"`).
445
479
  - `transitionToNext` is only valid when there is a "next" segment (i.e. it is not allowed on the last segment).
446
480
  - The total video duration must match the compiled timeline end:
447
481
  - `video.durationInFrames = sum(segment.durationInFrames) - sum(overlap)`
448
- - 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).
449
483
 
450
484
  Minimal example:
451
485
 
452
486
  ```json
453
487
  {
454
- "version": "2.0",
488
+ "version": "3.0",
455
489
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 60 },
490
+ "theme": { "preset": "studio-dark" },
456
491
  "segments": [
457
492
  {
458
493
  "id": "scene-1",
@@ -460,7 +495,7 @@ Minimal example:
460
495
  "root": {
461
496
  "id": "root",
462
497
  "type": "Scene",
463
- "props": { "background": { "type": "color", "value": "#000000" } },
498
+ "props": { "background": { "type": "color", "token": "background" } },
464
499
  "children": [{ "id": "t1", "type": "Text", "props": { "content": "Hello" } }]
465
500
  }
466
501
  }
@@ -471,7 +506,7 @@ Minimal example:
471
506
  Supported `transitionToNext.type` values (segment overlap transitions):
472
507
 
473
508
  - `FadeTransition`
474
- - `SlideTransition` (`props`: `{ direction: "left"|"right"|"up"|"down", distance?: number }`)
509
+ - `SlideTransition` (`props`: `{ direction: "left"|"right"|"up"|"down", distance?: "enter"|"nudge"|"travel" }`)
475
510
  - `ZoomTransition` (`props`: `{ type: "zoomIn"|"zoomOut" }`)
476
511
  - `WipeTransition` (`props`: `{ direction: "left"|"right"|"up"|"down"|"diagonal", softEdge?: boolean }`)
477
512
  - `CircularReveal` (`props`: `{ direction: "open"|"close", center?: { x: 0..1, y: 0..1 } }`)
@@ -487,14 +522,15 @@ Notes:
487
522
 
488
523
  ```json
489
524
  {
490
- "version": "2.0",
525
+ "version": "3.0",
491
526
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 60 },
527
+ "theme": { "preset": "studio-dark" },
492
528
  "timeline": [
493
529
  {
494
530
  "id": "scene",
495
531
  "type": "Scene",
496
532
  "timing": { "from": 0, "durationInFrames": 60 },
497
- "props": { "background": { "type": "color", "value": "#000000" } }
533
+ "props": { "background": { "type": "color", "token": "background" } }
498
534
  }
499
535
  ]
500
536
  }
@@ -622,7 +658,7 @@ node scripts/generate-readme-components.mjs
622
658
  | `TypewriterText` | composite | text | no | no | Character-by-character text reveal with optional blinking cursor |
623
659
  | `CircularReveal` | composite | transition | yes (1..∞) | no | Circular iris reveal/hide transition wrapper |
624
660
  | `FadeTransition` | composite | transition | yes (1..∞) | no | Fade in/out wrapper (used for segment transitions and overlays) |
625
- | `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 |
626
662
  | `WipeTransition` | composite | transition | yes (1..∞) | no | Directional wipe reveal/hide wrapper transition |
627
663
  | `ZoomTransition` | composite | transition | yes (1..∞) | no | Zoom in/out wrapper transition |
628
664
 
@@ -637,18 +673,25 @@ node scripts/generate-readme-components.mjs
637
673
  - internal: `false`
638
674
  - children: `no`
639
675
  - description: Branded intro scene (logo + company name + optional tagline)
640
- - 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.
641
677
 
642
678
  Props:
643
679
 
644
680
  | Prop | Type | Required | Default | Notes |
645
681
  | - | - | - | - | - |
646
- | `backgroundColor` | string | yes | "#000000" | |
682
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
647
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" | |
648
686
  | `logoSrc` | string | yes | | minLength=1 |
649
687
  | `musicTrack` | string | no | | |
650
- | `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" | |
651
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" | |
652
695
 
653
696
  ##### `LogoReveal`
654
697
 
@@ -657,15 +700,17 @@ Props:
657
700
  - internal: `false`
658
701
  - children: `no`
659
702
  - description: Logo intro animation (fade/scale/rotate/slide), optionally with a sound effect
660
- - 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.
661
704
 
662
705
  Props:
663
706
 
664
707
  | Prop | Type | Required | Default | Notes |
665
708
  | - | - | - | - | - |
666
- | `backgroundColor` | string | yes | "#000000" | |
709
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "background" | |
667
710
  | `effect` | enum("fade" \| "scale" \| "rotate" \| "slide") | yes | "scale" | |
711
+ | `logoSize` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl") | yes | "xl" | |
668
712
  | `logoSrc` | string | yes | | minLength=1 |
713
+ | `slideDistance` | enum("enter" \| "nudge" \| "travel") | yes | "travel" | |
669
714
  | `soundEffect` | string | no | | |
670
715
 
671
716
  ##### `OutroScene`
@@ -675,17 +720,30 @@ Props:
675
720
  - internal: `false`
676
721
  - children: `no`
677
722
  - description: End screen with logo, message, optional CTA buttons and social handles
678
- - 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).
679
724
 
680
725
  Props:
681
726
 
682
727
  | Prop | Type | Required | Default | Notes |
683
728
  | - | - | - | - | - |
684
- | `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" | |
685
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" | |
686
737
  | `logoSrc` | string | yes | | minLength=1 |
687
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" | |
688
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" | |
689
747
 
690
748
  ##### `Watermark`
691
749
 
@@ -694,18 +752,21 @@ Props:
694
752
  - internal: `false`
695
753
  - children: `no`
696
754
  - description: Persistent logo/text watermark in a corner
697
- - 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).
698
756
 
699
757
  Props:
700
758
 
701
759
  | Prop | Type | Required | Default | Notes |
702
760
  | - | - | - | - | - |
703
- | `color` | string | yes | "#FFFFFF" | |
704
- | `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" | |
705
764
  | `position` | enum("topLeft" \| "topRight" \| "bottomLeft" \| "bottomRight") | yes | "bottomRight" | |
706
- | `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" | |
707
766
  | `src` | string | no | | |
708
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" | |
709
770
  | `type` | enum("logo" \| "text") | yes | "logo" | |
710
771
 
711
772
  #### Category: `data`
@@ -717,21 +778,22 @@ Props:
717
778
  - internal: `false`
718
779
  - children: `no`
719
780
  - description: Animated numeric counter (spring or linear), optionally with an icon and suffix
720
- - 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).
721
782
 
722
783
  Props:
723
784
 
724
785
  | Prop | Type | Required | Default | Notes |
725
786
  | - | - | - | - | - |
726
787
  | `animationType` | enum("spring" \| "linear") | yes | "spring" | |
727
- | `color` | string | yes | "#FFFFFF" | |
728
- | `fontFamily` | string | yes | "Inter" | |
729
- | `fontSize` | number | yes | 96 | min=8, max=300 |
730
- | `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" | |
731
790
  | `from` | number | yes | 0 | |
732
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" | |
733
794
  | `suffix` | string | no | | |
734
795
  | `to` | number | yes | 100 | |
796
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
735
797
 
736
798
  ##### `BarChart`
737
799
 
@@ -740,17 +802,32 @@ Props:
740
802
  - internal: `false`
741
803
  - children: `no`
742
804
  - description: Animated bar chart (vertical or horizontal)
743
- - 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).
744
806
 
745
807
  Props:
746
808
 
747
809
  | Prop | Type | Required | Default | Notes |
748
810
  | - | - | - | - | - |
749
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" | |
750
819
  | `maxValue` | number | no | | |
751
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" | |
752
823
  | `showGrid` | boolean | yes | false | |
753
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" | |
754
831
 
755
832
  ##### `LineGraph`
756
833
 
@@ -759,18 +836,21 @@ Props:
759
836
  - internal: `false`
760
837
  - children: `no`
761
838
  - description: Animated line graph (SVG) with draw/reveal modes
762
- - 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).
763
840
 
764
841
  Props:
765
842
 
766
843
  | Prop | Type | Required | Default | Notes |
767
844
  | - | - | - | - | - |
768
845
  | `animate` | enum("draw" \| "reveal") | yes | "draw" | |
769
- | `color` | string | yes | "#00FF00" | |
846
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
770
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" | |
771
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" | |
772
852
  | `showDots` | boolean | yes | true | |
773
- | `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" | |
774
854
 
775
855
  ##### `ProgressBar`
776
856
 
@@ -779,18 +859,25 @@ Props:
779
859
  - internal: `false`
780
860
  - children: `no`
781
861
  - description: Animated progress bar that fills over the component duration
782
- - 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).
783
863
 
784
864
  Props:
785
865
 
786
866
  | Prop | Type | Required | Default | Notes |
787
867
  | - | - | - | - | - |
788
- | `backgroundColor` | string | yes | "rgba(255,255,255,0.2)" | |
789
- | `color` | string | yes | "#00FF00" | |
790
- | `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" | |
791
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" | |
792
877
  | `position` | enum("top" \| "bottom") | yes | "bottom" | |
878
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "full" | |
793
879
  | `showPercentage` | boolean | yes | true | |
880
+ | `trackColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "muted" | |
794
881
 
795
882
  ##### `ProgressRing`
796
883
 
@@ -799,18 +886,22 @@ Props:
799
886
  - internal: `false`
800
887
  - children: `no`
801
888
  - description: Circular progress indicator (SVG) that animates from 0 to percentage over duration
802
- - 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).
803
890
 
804
891
  Props:
805
892
 
806
893
  | Prop | Type | Required | Default | Notes |
807
894
  | - | - | - | - | - |
808
- | `backgroundColor` | string | yes | "rgba(255,255,255,0.2)" | |
809
- | `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" | |
810
900
  | `percentage` | number | yes | | min=0, max=100 |
811
901
  | `showLabel` | boolean | yes | true | |
812
- | `size` | number | yes | 200 | min=100, max=500 |
813
- | `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" | |
814
905
 
815
906
  #### Category: `image`
816
907
 
@@ -827,7 +918,7 @@ Props:
827
918
 
828
919
  | Prop | Type | Required | Default | Notes |
829
920
  | - | - | - | - | - |
830
- | `borderRadius` | number | yes | 0 | min=0 |
921
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
831
922
  | `fit` | enum("cover" \| "contain") | yes | "cover" | |
832
923
  | `opacity` | number | yes | 1 | min=0, max=1 |
833
924
  | `src` | string | yes | | minLength=1 |
@@ -839,15 +930,28 @@ Props:
839
930
  - internal: `false`
840
931
  - children: `no`
841
932
  - description: Collage of multiple images in a grid/stack/scatter layout with staggered entrances
842
- - 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).
843
934
 
844
935
  Props:
845
936
 
846
937
  | Prop | Type | Required | Default | Notes |
847
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" | |
848
949
  | `images` | array<object> | yes | | minItems=2, maxItems=9 |
849
950
  | `layout` | enum("grid" \| "stack" \| "scatter") | yes | "grid" | |
850
- | `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" | |
851
955
 
852
956
  ##### `ImageReveal`
853
957
 
@@ -891,15 +995,21 @@ Props:
891
995
  - internal: `false`
892
996
  - children: `no`
893
997
  - description: Image with a caption strip (top/bottom) or overlay caption
894
- - 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).
895
999
 
896
1000
  Props:
897
1001
 
898
1002
  | Prop | Type | Required | Default | Notes |
899
1003
  | - | - | - | - | - |
900
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" | |
901
1009
  | `captionPosition` | enum("top" \| "bottom" \| "overlay") | yes | "bottom" | |
902
- | `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" | |
903
1013
  | `src` | string | yes | | minLength=1 |
904
1014
 
905
1015
  ##### `KenBurnsImage`
@@ -909,17 +1019,16 @@ Props:
909
1019
  - internal: `false`
910
1020
  - children: `no`
911
1021
  - description: Slow zoom and pan (Ken Burns effect) for a still image
912
- - 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).
913
1023
 
914
1024
  Props:
915
1025
 
916
1026
  | Prop | Type | Required | Default | Notes |
917
1027
  | - | - | - | - | - |
918
- | `endScale` | number | yes | 1.2 | min=1, max=2 |
919
- | `panAmount` | number | yes | 50 | min=0, max=100 |
1028
+ | `mode` | enum("zoomIn" \| "zoomOut" \| "none") | yes | "zoomIn" | |
920
1029
  | `panDirection` | enum("none" \| "left" \| "right" \| "up" \| "down") | yes | "none" | |
1030
+ | `panDistance` | enum("enter" \| "nudge" \| "travel") | yes | "nudge" | |
921
1031
  | `src` | string | yes | | minLength=1 |
922
- | `startScale` | number | yes | 1 | min=1, max=2 |
923
1032
 
924
1033
  #### Category: `layout`
925
1034
 
@@ -936,11 +1045,11 @@ Props:
936
1045
 
937
1046
  | Prop | Type | Required | Default | Notes |
938
1047
  | - | - | - | - | - |
939
- | `backgroundColor` | string | no | | |
940
- | `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" | |
941
1050
  | `height` | number | no | | |
942
1051
  | `opacity` | number | yes | 1 | min=0, max=1 |
943
- | `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" | |
944
1053
  | `width` | number | no | | |
945
1054
 
946
1055
  ##### `CardStack`
@@ -950,14 +1059,26 @@ Props:
950
1059
  - internal: `false`
951
1060
  - children: `no`
952
1061
  - description: Sequential stacked cards (2-5) with flip/slide/fade transitions
953
- - 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).
954
1063
 
955
1064
  Props:
956
1065
 
957
1066
  | Prop | Type | Required | Default | Notes |
958
1067
  | - | - | - | - | - |
1068
+ | `background` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "surface" | |
959
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" | |
960
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" | |
961
1082
  | `transition` | enum("flip" \| "slide" \| "fade") | yes | "flip" | |
962
1083
 
963
1084
  ##### `Frame`
@@ -973,11 +1094,11 @@ Props:
973
1094
 
974
1095
  | Prop | Type | Required | Default | Notes |
975
1096
  | - | - | - | - | - |
976
- | `backgroundColor` | string | no | | |
977
- | `borderRadius` | number | yes | 0 | min=0 |
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" | |
978
1099
  | `height` | number | no | | |
979
1100
  | `opacity` | number | yes | 1 | min=0, max=1 |
980
- | `padding` | number | yes | 0 | min=0 |
1101
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
981
1102
  | `width` | number | no | | |
982
1103
  | `x` | number | yes | 0 | |
983
1104
  | `y` | number | yes | 0 | |
@@ -997,9 +1118,9 @@ Props:
997
1118
  | - | - | - | - | - |
998
1119
  | `align` | enum("start" \| "center" \| "end" \| "stretch") | yes | "stretch" | |
999
1120
  | `columns` | integer | yes | 2 | min=1, max=12 |
1000
- | `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" | |
1001
1122
  | `justify` | enum("start" \| "center" \| "end" \| "stretch") | yes | "stretch" | |
1002
- | `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" | |
1003
1124
  | `rows` | integer | yes | 1 | min=1, max=12 |
1004
1125
 
1005
1126
  ##### `GridLayout`
@@ -1016,8 +1137,8 @@ Props:
1016
1137
  | Prop | Type | Required | Default | Notes |
1017
1138
  | - | - | - | - | - |
1018
1139
  | `columns` | integer | yes | 2 | min=1, max=4 |
1019
- | `gap` | number | yes | 20 | min=0, max=50 |
1020
- | `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" | |
1021
1142
  | `rows` | integer | yes | 2 | min=1, max=4 |
1022
1143
 
1023
1144
  ##### `Layer`
@@ -1033,7 +1154,7 @@ Props:
1033
1154
 
1034
1155
  | Prop | Type | Required | Default | Notes |
1035
1156
  | - | - | - | - | - |
1036
- | `inset` | number | yes | 0 | min=0 |
1157
+ | `inset` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "0" | |
1037
1158
  | `opacity` | number | yes | 1 | min=0, max=1 |
1038
1159
  | `pointerEvents` | enum("none" \| "auto") | yes | "none" | |
1039
1160
  | `zIndex` | integer | yes | 0 | min=-9007199254740991, max=9007199254740991 |
@@ -1096,12 +1217,13 @@ Props:
1096
1217
 
1097
1218
  | Prop | Type | Required | Default | Notes |
1098
1219
  | - | - | - | - | - |
1099
- | `fill` | string | yes | "#FFFFFF" | |
1220
+ | `fill` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1100
1221
  | `height` | number | yes | 100 | |
1101
1222
  | `opacity` | number | yes | 1 | min=0, max=1 |
1223
+ | `radius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
1102
1224
  | `shape` | enum("rect" \| "circle") | yes | "rect" | |
1103
- | `strokeColor` | string | no | | |
1104
- | `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" | |
1105
1227
  | `width` | number | yes | 100 | |
1106
1228
  | `x` | number | yes | 0 | |
1107
1229
  | `y` | number | yes | 0 | |
@@ -1119,10 +1241,12 @@ Props:
1119
1241
 
1120
1242
  | Prop | Type | Required | Default | Notes |
1121
1243
  | - | - | - | - | - |
1122
- | `dividerColor` | string | no | | |
1123
- | `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" | |
1124
1248
  | `orientation` | enum("vertical" \| "horizontal") | yes | "vertical" | |
1125
- | `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" | |
1126
1250
  | `split` | number | yes | 0.5 | min=0.1, max=0.9 |
1127
1251
 
1128
1252
  ##### `Stack`
@@ -1140,9 +1264,9 @@ Props:
1140
1264
  | - | - | - | - | - |
1141
1265
  | `align` | enum("start" \| "center" \| "end" \| "stretch") | yes | "center" | |
1142
1266
  | `direction` | enum("row" \| "column") | yes | "column" | |
1143
- | `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" | |
1144
1268
  | `justify` | enum("start" \| "center" \| "end" \| "between") | yes | "center" | |
1145
- | `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" | |
1146
1270
 
1147
1271
  ##### `ThirdLowerBanner`
1148
1272
 
@@ -1157,14 +1281,28 @@ Props:
1157
1281
 
1158
1282
  | Prop | Type | Required | Default | Notes |
1159
1283
  | - | - | - | - | - |
1160
- | `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" | |
1161
1287
  | `avatarSrc` | string | no | | |
1162
- | `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" | |
1163
1292
  | `name` | string | yes | | maxLength=50 |
1164
- | `primaryColor` | string | yes | "#FFFFFF" | |
1165
- | `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" | |
1166
1300
  | `showAvatar` | boolean | yes | false | |
1167
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" | |
1168
1306
 
1169
1307
  #### Category: `media`
1170
1308
 
@@ -1200,7 +1338,7 @@ Props:
1200
1338
 
1201
1339
  | Prop | Type | Required | Default | Notes |
1202
1340
  | - | - | - | - | - |
1203
- | `borderRadius` | number | yes | 0 | min=0 |
1341
+ | `borderRadius` | enum("none" \| "sm" \| "md" \| "lg" \| "full") | yes | "none" | |
1204
1342
  | `fit` | enum("cover" \| "contain") | yes | "cover" | |
1205
1343
  | `muted` | boolean | yes | true | |
1206
1344
  | `opacity` | number | yes | 1 | min=0, max=1 |
@@ -1213,7 +1351,7 @@ Props:
1213
1351
  - internal: `false`
1214
1352
  - children: `no`
1215
1353
  - description: Video background with an optional overlay (text/logo/gradient)
1216
- - 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).
1217
1355
 
1218
1356
  Props:
1219
1357
 
@@ -1233,19 +1371,38 @@ Props:
1233
1371
  - internal: `false`
1234
1372
  - children: `no`
1235
1373
  - description: Instagram story-style layout with profile header, text overlay, and optional sticker
1236
- - 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).
1237
1375
 
1238
1376
  Props:
1239
1377
 
1240
1378
  | Prop | Type | Required | Default | Notes |
1241
1379
  | - | - | - | - | - |
1242
- | `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" | |
1243
1382
  | `backgroundImage` | string | no | | |
1383
+ | `headerGap` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "4" | |
1244
1384
  | `musicTrack` | string | no | | |
1385
+ | `padding` | enum("0" \| "1" \| "2" \| "3" \| "4" \| "5" \| "6" \| "8" \| "10" \| "12" \| "16" \| "20" \| "24" \| "32") | yes | "16" | |
1245
1386
  | `profilePic` | string | no | | |
1246
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" | |
1247
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" | |
1248
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" | |
1249
1406
 
1250
1407
  ##### `TikTokCaption`
1251
1408
 
@@ -1254,21 +1411,22 @@ Props:
1254
1411
  - internal: `false`
1255
1412
  - children: `no`
1256
1413
  - description: TikTok-style captions with stroke and optional word highlighting
1257
- - 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).
1258
1415
 
1259
1416
  Props:
1260
1417
 
1261
1418
  | Prop | Type | Required | Default | Notes |
1262
1419
  | - | - | - | - | - |
1263
- | `color` | string | yes | "#FFFFFF" | |
1264
- | `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" | |
1265
1423
  | `highlightStyle` | enum("word" \| "bounce" \| "none") | yes | "word" | |
1266
- | `maxWidthPct` | number | yes | 0.92 | min=0.1, max=1 |
1267
1424
  | `position` | enum("center" \| "bottom") | yes | "center" | |
1268
- | `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
1269
- | `strokeColor` | string | yes | "#000000" | |
1270
- | `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" | |
1271
1428
  | `text` | string | yes | | maxLength=150 |
1429
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "black" | |
1272
1430
 
1273
1431
  ##### `TwitterCard`
1274
1432
 
@@ -1277,19 +1435,39 @@ Props:
1277
1435
  - internal: `false`
1278
1436
  - children: `no`
1279
1437
  - description: Twitter/X post card layout with author header and optional image
1280
- - 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).
1281
1439
 
1282
1440
  Props:
1283
1441
 
1284
1442
  | Prop | Type | Required | Default | Notes |
1285
1443
  | - | - | - | - | - |
1286
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" | |
1287
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" | |
1288
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" | |
1289
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" | |
1290
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" | |
1291
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" | |
1292
1469
  | `verified` | boolean | yes | false | |
1470
+ | `verifiedColor` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "primary" | |
1293
1471
 
1294
1472
  ##### `YouTubeThumbnail`
1295
1473
 
@@ -1298,18 +1476,26 @@ Props:
1298
1476
  - internal: `false`
1299
1477
  - children: `no`
1300
1478
  - description: YouTube-style thumbnail layout (16:9) with bold title and optional face cutout
1301
- - 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).
1302
1480
 
1303
1481
  Props:
1304
1482
 
1305
1483
  | Prop | Type | Required | Default | Notes |
1306
1484
  | - | - | - | - | - |
1307
- | `accentColor` | string | yes | "#FF0000" | |
1308
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" | |
1309
1493
  | `style` | enum("bold" \| "minimal" \| "dramatic") | yes | "bold" | |
1310
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" | |
1311
1496
  | `thumbnailFace` | string | no | | |
1312
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" | |
1313
1499
 
1314
1500
  #### Category: `text`
1315
1501
 
@@ -1320,23 +1506,23 @@ Props:
1320
1506
  - internal: `false`
1321
1507
  - children: `no`
1322
1508
  - description: Counts from a start value to an end value with formatting options
1323
- - 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).
1324
1510
 
1325
1511
  Props:
1326
1512
 
1327
1513
  | Prop | Type | Required | Default | Notes |
1328
1514
  | - | - | - | - | - |
1329
- | `color` | string | yes | "#FFFFFF" | |
1515
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1330
1516
  | `decimals` | integer | yes | 0 | min=0, max=4 |
1331
- | `fontFamily` | string | yes | "Inter" | |
1332
- | `fontSize` | number | yes | 72 | min=8, max=240 |
1333
- | `fontWeight` | integer | yes | 700 | min=100, max=900 |
1517
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1334
1518
  | `format` | enum("integer" \| "decimal" \| "currency" \| "percentage") | yes | "integer" | |
1335
1519
  | `from` | number | yes | 0 | |
1336
1520
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1337
1521
  | `prefix` | string | no | | |
1522
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "4xl" | |
1338
1523
  | `suffix` | string | no | | |
1339
1524
  | `to` | number | yes | 100 | |
1525
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1340
1526
 
1341
1527
  ##### `GlitchText`
1342
1528
 
@@ -1345,19 +1531,26 @@ Props:
1345
1531
  - internal: `false`
1346
1532
  - children: `no`
1347
1533
  - description: Cyberpunk-style glitch text with RGB split jitter
1348
- - 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.
1349
1535
 
1350
1536
  Props:
1351
1537
 
1352
1538
  | Prop | Type | Required | Default | Notes |
1353
1539
  | - | - | - | - | - |
1354
- | `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" | |
1355
1545
  | `content` | string | yes | | maxLength=100 |
1356
- | `fontFamily` | string | yes | "monospace" | |
1357
- | `fontSize` | number | yes | 72 | min=8, max=240 |
1358
- | `glitchDuration` | integer | yes | 10 | min=5, max=30 |
1546
+ | `font` | enum("display" \| "body" \| "mono") | yes | "mono" | |
1547
+ | `glitchDuration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1359
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" | |
1360
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" | |
1361
1554
 
1362
1555
  ##### `KineticTypography`
1363
1556
 
@@ -1366,15 +1559,15 @@ Props:
1366
1559
  - internal: `false`
1367
1560
  - children: `no`
1368
1561
  - description: Rhythmic single-word kinetic typography driven by a timing array
1369
- - 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).
1370
1563
 
1371
1564
  Props:
1372
1565
 
1373
1566
  | Prop | Type | Required | Default | Notes |
1374
1567
  | - | - | - | - | - |
1375
- | `color` | string | yes | "#FFFFFF" | |
1376
- | `fontFamily` | string | yes | "Inter" | |
1377
- | `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" | |
1378
1571
  | `timing` | array<integer> | yes | | minItems=1 |
1379
1572
  | `transition` | enum("fade" \| "scale" \| "slideLeft" \| "slideRight") | yes | "scale" | |
1380
1573
  | `words` | array<object> | yes | | minItems=1, maxItems=50 |
@@ -1386,7 +1579,7 @@ Props:
1386
1579
  - internal: `false`
1387
1580
  - children: `no`
1388
1581
  - description: Outlined title text with simple draw/fill animation
1389
- - 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).
1390
1583
 
1391
1584
  Props:
1392
1585
 
@@ -1394,13 +1587,13 @@ Props:
1394
1587
  | - | - | - | - | - |
1395
1588
  | `animation` | enum("draw" \| "fill") | yes | "draw" | |
1396
1589
  | `content` | string | yes | | maxLength=50 |
1397
- | `fillColor` | string | yes | "#000000" | |
1398
- | `fontFamily` | string | yes | "Inter" | |
1399
- | `fontSize` | number | yes | 96 | min=8, max=240 |
1400
- | `fontWeight` | integer | yes | 800 | min=100, max=900 |
1401
- | `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" | |
1402
1593
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1403
- | `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" | |
1404
1597
 
1405
1598
  ##### `SplitText`
1406
1599
 
@@ -1416,15 +1609,14 @@ Props:
1416
1609
  | Prop | Type | Required | Default | Notes |
1417
1610
  | - | - | - | - | - |
1418
1611
  | `animation` | enum("fade" \| "slideUp" \| "slideDown" \| "scale" \| "rotate") | yes | "slideUp" | |
1419
- | `color` | string | yes | "#FFFFFF" | |
1612
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1420
1613
  | `content` | string | yes | | maxLength=200 |
1421
- | `fontFamily` | string | yes | "Inter" | |
1422
- | `fontSize` | number | yes | 48 | min=8, max=200 |
1423
- | `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
1614
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1424
1615
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1425
- | `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
1616
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1426
1617
  | `splitBy` | enum("word" \| "letter") | yes | "word" | |
1427
- | `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" | |
1428
1620
 
1429
1621
  ##### `SubtitleText`
1430
1622
 
@@ -1433,21 +1625,24 @@ Props:
1433
1625
  - internal: `false`
1434
1626
  - children: `no`
1435
1627
  - description: Caption/subtitle box with fade in/out and optional highlighted words
1436
- - 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).
1437
1629
 
1438
1630
  Props:
1439
1631
 
1440
1632
  | Prop | Type | Required | Default | Notes |
1441
1633
  | - | - | - | - | - |
1442
- | `backgroundColor` | string | yes | "rgba(0,0,0,0.7)" | |
1443
- | `color` | string | yes | "#FFFFFF" | |
1444
- | `fontFamily` | string | yes | "Inter" | |
1445
- | `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" | |
1446
1639
  | `highlightWords` | array<string> | no | | |
1447
- | `maxWidth` | number | yes | 800 | min=200, max=1200 |
1448
- | `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" | |
1449
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" | |
1450
1644
  | `text` | string | yes | | maxLength=200 |
1645
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "bold" | |
1451
1646
 
1452
1647
  ##### `Text`
1453
1648
 
@@ -1463,14 +1658,15 @@ Props:
1463
1658
  | Prop | Type | Required | Default | Notes |
1464
1659
  | - | - | - | - | - |
1465
1660
  | `animation` | enum("none" \| "fade" \| "slide" \| "zoom") | yes | "fade" | |
1466
- | `color` | string | yes | "#FFFFFF" | |
1661
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1467
1662
  | `content` | string | yes | | |
1468
- | `fontFamily` | string | yes | "Inter" | |
1469
- | `fontSize` | number | yes | 48 | |
1470
- | `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
1663
+ | `font` | enum("display" \| "body" \| "mono") | yes | "display" | |
1664
+ | `lineHeight` | enum("tight" \| "normal" \| "relaxed") | yes | "normal" | |
1471
1665
  | `position` | enum("top" \| "center" \| "bottom" \| "left" \| "right") | yes | "center" | |
1472
- | `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
1666
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1473
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" | |
1474
1670
 
1475
1671
  ##### `TypewriterText`
1476
1672
 
@@ -1485,16 +1681,15 @@ Props:
1485
1681
 
1486
1682
  | Prop | Type | Required | Default | Notes |
1487
1683
  | - | - | - | - | - |
1488
- | `color` | string | yes | "#FFFFFF" | |
1684
+ | `color` | enum("background" \| "foreground" \| "surface" \| "surfaceForeground" \| "muted" \| "mutedForeground" \| "primary" \| "primaryForeground" \| "secondary" \| "secondaryForeground" \| "accent" \| "accentForeground" \| "border" \| "ring") | yes | "foreground" | |
1489
1685
  | `content` | string | yes | | maxLength=500 |
1490
- | `cursorColor` | string | yes | "#FFFFFF" | |
1491
- | `fontFamily` | string | yes | "Inter" | |
1492
- | `fontSize` | number | yes | 48 | min=8, max=200 |
1493
- | `maxWidthPct` | number | yes | 0.9 | min=0.1, max=1 |
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" | |
1494
1688
  | `position` | enum("top" \| "center" \| "bottom") | yes | "center" | |
1495
- | `safeInsetPct` | number | yes | 0.06 | min=0, max=0.25 |
1496
1689
  | `showCursor` | boolean | yes | true | |
1690
+ | `size` | enum("xs" \| "sm" \| "md" \| "lg" \| "xl" \| "2xl" \| "3xl" \| "4xl" \| "5xl" \| "6xl") | yes | "2xl" | |
1497
1691
  | `speed` | number | yes | 2 | min=0.5, max=5 |
1692
+ | `weight` | enum("regular" \| "medium" \| "semibold" \| "bold" \| "black") | yes | "semibold" | |
1498
1693
 
1499
1694
  #### Category: `transition`
1500
1695
 
@@ -1505,7 +1700,7 @@ Props:
1505
1700
  - internal: `false`
1506
1701
  - children: `yes (1..∞)`
1507
1702
  - description: Circular iris reveal/hide transition wrapper
1508
- - 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.
1509
1704
 
1510
1705
  Props:
1511
1706
 
@@ -1513,7 +1708,7 @@ Props:
1513
1708
  | - | - | - | - | - |
1514
1709
  | `center` | object | no | | additionalProperties=false |
1515
1710
  | `direction` | enum("open" \| "close") | yes | "open" | |
1516
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1711
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1517
1712
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1518
1713
 
1519
1714
  ##### `FadeTransition`
@@ -1523,13 +1718,13 @@ Props:
1523
1718
  - internal: `false`
1524
1719
  - children: `yes (1..∞)`
1525
1720
  - description: Fade in/out wrapper (used for segment transitions and overlays)
1526
- - llmGuidance: Use for gentle transitions. durationInFrames ~30 is standard.
1721
+ - llmGuidance: Use for gentle transitions. duration uses theme motion tokens.
1527
1722
 
1528
1723
  Props:
1529
1724
 
1530
1725
  | Prop | Type | Required | Default | Notes |
1531
1726
  | - | - | - | - | - |
1532
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1727
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1533
1728
  | `easing` | enum("linear" \| "easeIn" \| "easeOut" \| "easeInOut") | yes | "easeInOut" | |
1534
1729
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1535
1730
 
@@ -1539,16 +1734,16 @@ Props:
1539
1734
  - category: `transition`
1540
1735
  - internal: `false`
1541
1736
  - children: `yes (1..∞)`
1542
- - description: Slide in/out wrapper (used for segment transitions and overlays)
1543
- - 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.
1544
1739
 
1545
1740
  Props:
1546
1741
 
1547
1742
  | Prop | Type | Required | Default | Notes |
1548
1743
  | - | - | - | - | - |
1549
1744
  | `direction` | enum("left" \| "right" \| "up" \| "down") | yes | "left" | |
1550
- | `distance` | integer | yes | 160 | min=1, max=2000 |
1551
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1745
+ | `distance` | enum("enter" \| "nudge" \| "travel") | yes | "travel" | |
1746
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1552
1747
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1553
1748
 
1554
1749
  ##### `WipeTransition`
@@ -1558,14 +1753,14 @@ Props:
1558
1753
  - internal: `false`
1559
1754
  - children: `yes (1..∞)`
1560
1755
  - description: Directional wipe reveal/hide wrapper transition
1561
- - 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.
1562
1757
 
1563
1758
  Props:
1564
1759
 
1565
1760
  | Prop | Type | Required | Default | Notes |
1566
1761
  | - | - | - | - | - |
1567
1762
  | `direction` | enum("left" \| "right" \| "up" \| "down" \| "diagonal") | yes | "right" | |
1568
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1763
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1569
1764
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1570
1765
  | `softEdge` | boolean | yes | false | |
1571
1766
 
@@ -1576,13 +1771,13 @@ Props:
1576
1771
  - internal: `false`
1577
1772
  - children: `yes (1..∞)`
1578
1773
  - description: Zoom in/out wrapper transition
1579
- - 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.
1580
1775
 
1581
1776
  Props:
1582
1777
 
1583
1778
  | Prop | Type | Required | Default | Notes |
1584
1779
  | - | - | - | - | - |
1585
- | `durationInFrames` | integer | yes | 30 | min=10, max=60 |
1780
+ | `duration` | enum("fast" \| "base" \| "slow") | yes | "fast" | |
1586
1781
  | `phase` | enum("in" \| "out" \| "inOut") | yes | "inOut" | |
1587
1782
  | `type` | enum("zoomIn" \| "zoomOut") | yes | "zoomIn" | |
1588
1783
 
@@ -1595,30 +1790,30 @@ The `examples/` directory contains validated IR JSON files you can use as starti
1595
1790
  Quick validate:
1596
1791
 
1597
1792
  ```bash
1598
- npx waves validate --in examples/basic.v2.json
1793
+ npx waves validate --in examples/basic.v3.json
1599
1794
  ```
1600
1795
 
1601
1796
  Validate all examples:
1602
1797
 
1603
1798
  ```powershell
1604
- 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 }
1605
1800
  ```
1606
1801
 
1607
1802
  Render (writes an MP4; `examples/*.mp4` are gitignored):
1608
1803
 
1609
1804
  ```bash
1610
- 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
1611
1806
  ```
1612
1807
 
1613
1808
  If an example references `/assets/...`, you must provide those files and pass `--publicDir`:
1614
1809
 
1615
1810
  ```bash
1616
- 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
1617
1812
  ```
1618
1813
 
1619
1814
  ### Example 0: basic starter (segments + composites)
1620
1815
 
1621
- File: `examples/basic.v2.json`
1816
+ File: `examples/basic.v3.json`
1622
1817
 
1623
1818
  This is the default starter IR used by `waves write-ir --template basic`. It demonstrates:
1624
1819
 
@@ -1629,60 +1824,33 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1629
1824
 
1630
1825
  ```json
1631
1826
  {
1632
- "version": "2.0",
1633
- "video": {
1634
- "id": "main",
1635
- "width": 1920,
1636
- "height": 1080,
1637
- "fps": 30,
1638
- "durationInFrames": 165
1639
- },
1827
+ "version": "3.0",
1828
+ "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 168 },
1829
+ "theme": { "preset": "studio-dark" },
1640
1830
  "segments": [
1641
1831
  {
1642
1832
  "id": "scene-1",
1643
1833
  "durationInFrames": 90,
1644
- "transitionToNext": {
1645
- "type": "FadeTransition",
1646
- "durationInFrames": 15
1647
- },
1834
+ "transitionToNext": { "type": "FadeTransition", "duration": "fast" },
1648
1835
  "root": {
1649
1836
  "id": "root",
1650
1837
  "type": "Scene",
1651
- "props": {
1652
- "background": { "type": "color", "value": "#000000" }
1653
- },
1838
+ "props": { "background": { "type": "color", "token": "background" } },
1654
1839
  "children": [
1655
1840
  {
1656
1841
  "id": "title",
1657
1842
  "type": "SplitText",
1658
- "props": {
1659
- "content": "Waves v0.3.0",
1660
- "fontSize": 96,
1661
- "splitBy": "word",
1662
- "stagger": 3,
1663
- "animation": "slideUp"
1664
- }
1843
+ "props": { "content": "Waves v0.5.0", "size": "5xl", "color": "foreground", "font": "display", "weight": "black", "splitBy": "word", "stagger": "fast", "animation": "slideUp" }
1665
1844
  },
1666
1845
  {
1667
1846
  "id": "subtitle",
1668
1847
  "type": "TypewriterText",
1669
- "props": {
1670
- "content": "Seamless alignment + overlays",
1671
- "fontSize": 48,
1672
- "position": "bottom",
1673
- "speed": 1.5
1674
- }
1848
+ "props": { "content": "Unified tokens + presets", "size": "2xl", "color": "mutedForeground", "font": "body", "weight": "semibold", "position": "bottom", "speed": 1.5 }
1675
1849
  },
1676
1850
  {
1677
1851
  "id": "wm",
1678
1852
  "type": "Watermark",
1679
- "props": {
1680
- "type": "text",
1681
- "text": "@depths.ai",
1682
- "position": "bottomRight",
1683
- "opacity": 0.4,
1684
- "size": 60
1685
- }
1853
+ "props": { "type": "text", "text": "@depths.ai", "position": "bottomRight", "opacity": "subtle", "size": "16", "color": "mutedForeground", "textSize": "lg" }
1686
1854
  }
1687
1855
  ]
1688
1856
  }
@@ -1693,25 +1861,11 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1693
1861
  "root": {
1694
1862
  "id": "root-2",
1695
1863
  "type": "Scene",
1696
- "props": {
1697
- "background": { "type": "color", "value": "#0B1220" }
1698
- },
1864
+ "props": { "background": { "type": "color", "token": "surface" } },
1699
1865
  "children": [
1700
- {
1701
- "id": "lower-third",
1702
- "type": "ThirdLowerBanner",
1703
- "props": { "name": "Waves", "title": "v0.3.0 - Alignment + overlays", "accentColor": "#3B82F6" }
1704
- },
1705
- {
1706
- "id": "count",
1707
- "type": "AnimatedCounter",
1708
- "props": { "from": 0, "to": 35, "suffix": " components", "fontSize": 96, "color": "#FFFFFF" }
1709
- },
1710
- {
1711
- "id": "wm-2",
1712
- "type": "Watermark",
1713
- "props": { "type": "text", "text": "waves", "position": "topLeft", "opacity": 0.25, "size": 52 }
1714
- }
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" } }
1715
1869
  ]
1716
1870
  }
1717
1871
  }
@@ -1721,14 +1875,15 @@ This is the default starter IR used by `waves write-ir --template basic`. It dem
1721
1875
 
1722
1876
  ### Example 1: primitives-only layout
1723
1877
 
1724
- File: `examples/primitives-card.v2.json`
1878
+ File: `examples/primitives-card.v3.json`
1725
1879
 
1726
1880
  This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1727
1881
 
1728
1882
  ```json
1729
1883
  {
1730
- "version": "2.0",
1884
+ "version": "3.0",
1731
1885
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 150 },
1886
+ "theme": { "preset": "studio-dark" },
1732
1887
  "segments": [
1733
1888
  {
1734
1889
  "id": "card",
@@ -1736,49 +1891,49 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1736
1891
  "root": {
1737
1892
  "id": "scene",
1738
1893
  "type": "Scene",
1739
- "props": { "background": { "type": "color", "value": "#0B1220" } },
1894
+ "props": { "background": { "type": "color", "token": "surface" } },
1740
1895
  "children": [
1741
- { "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 } },
1742
1897
  {
1743
1898
  "id": "panel",
1744
1899
  "type": "Frame",
1745
- "props": { "x": 192, "y": 200, "width": 1548, "height": 680, "padding": 0, "backgroundColor": "rgba(255,255,255,0.06)", "borderRadius": 36, "opacity": 1 },
1900
+ "props": { "x": 192, "y": 200, "width": 1548, "height": 680, "padding": "0", "backgroundColor": "surface", "borderRadius": "lg", "opacity": 0.7 },
1746
1901
  "children": [
1747
1902
  {
1748
1903
  "id": "panel-grid",
1749
1904
  "type": "Grid",
1750
- "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" },
1751
1906
  "children": [
1752
1907
  {
1753
1908
  "id": "tile-1",
1754
1909
  "type": "Box",
1755
- "props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1910
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1756
1911
  "children": [
1757
- { "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" } }
1758
1913
  ]
1759
1914
  },
1760
1915
  {
1761
1916
  "id": "tile-2",
1762
1917
  "type": "Box",
1763
- "props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1918
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1764
1919
  "children": [
1765
- { "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" } }
1766
1921
  ]
1767
1922
  },
1768
1923
  {
1769
1924
  "id": "tile-3",
1770
1925
  "type": "Box",
1771
- "props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1926
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1772
1927
  "children": [
1773
- { "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" } }
1774
1929
  ]
1775
1930
  },
1776
1931
  {
1777
1932
  "id": "tile-4",
1778
1933
  "type": "Box",
1779
- "props": { "padding": 40, "backgroundColor": "rgba(255,255,255,0.07)", "borderRadius": 24 },
1934
+ "props": { "padding": "10", "backgroundColor": "muted", "borderRadius": "lg", "opacity": 0.8 },
1780
1935
  "children": [
1781
- { "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" } }
1782
1937
  ]
1783
1938
  }
1784
1939
  ]
@@ -1790,10 +1945,12 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1790
1945
  "type": "Text",
1791
1946
  "props": {
1792
1947
  "content": "This entire layout is built from primitives only.",
1793
- "fontSize": 34,
1794
1948
  "position": "bottom",
1795
1949
  "animation": "fade",
1796
- "color": "#C7D2FE"
1950
+ "size": "xl",
1951
+ "color": "mutedForeground",
1952
+ "font": "body",
1953
+ "weight": "medium"
1797
1954
  }
1798
1955
  }
1799
1956
  ]
@@ -1807,64 +1964,65 @@ This example uses only primitives: `Scene`, `Box`, `Grid`, `Shape`, `Text`.
1807
1964
 
1808
1965
  When authoring segments with overlaps, the total end time is:
1809
1966
 
1810
- `sum(segments[i].durationInFrames) - sum(segments[i].transitionToNext.durationInFrames)`
1967
+ `sum(segments[i].durationInFrames) - sum(theme.motion.durationsInFrames[segments[i].transitionToNext.duration])`
1811
1968
 
1812
1969
  Example 2 has 4 segments:
1813
1970
 
1814
1971
  - segment durations: 90 + 90 + 90 + 90 = 360
1815
- - overlap durations: 15 + 15 + 15 = 45
1816
- - 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`)
1817
1974
 
1818
1975
  ### Example 2: segment transitions showcase
1819
1976
 
1820
- File: `examples/transitions-showcase.v2.json`
1977
+ File: `examples/transitions-showcase.v3.json`
1821
1978
 
1822
1979
  This example shows how to chain segments with overlaps using `transitionToNext`.
1823
1980
 
1824
1981
  ```json
1825
1982
  {
1826
- "version": "2.0",
1827
- "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" },
1828
1986
  "segments": [
1829
1987
  {
1830
1988
  "id": "s1",
1831
1989
  "durationInFrames": 90,
1832
- "transitionToNext": { "type": "FadeTransition", "durationInFrames": 15 },
1990
+ "transitionToNext": { "type": "FadeTransition", "duration": "base" },
1833
1991
  "root": {
1834
1992
  "id": "scene-1",
1835
1993
  "type": "Scene",
1836
- "props": { "background": { "type": "color", "value": "#000000" } },
1994
+ "props": { "background": { "type": "color", "token": "background" } },
1837
1995
  "children": [
1838
- { "id": "title-1", "type": "SplitText", "props": { "content": "FadeTransition", "fontSize": 100, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "center" } },
1839
- { "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" } }
1840
1998
  ]
1841
1999
  }
1842
2000
  },
1843
2001
  {
1844
2002
  "id": "s2",
1845
2003
  "durationInFrames": 90,
1846
- "transitionToNext": { "type": "SlideTransition", "durationInFrames": 15, "props": { "direction": "left", "distance": 120 } },
2004
+ "transitionToNext": { "type": "SlideTransition", "duration": "base", "props": { "direction": "left", "distance": "travel" } },
1847
2005
  "root": {
1848
2006
  "id": "scene-2",
1849
2007
  "type": "Scene",
1850
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2008
+ "props": { "background": { "type": "color", "token": "surface" } },
1851
2009
  "children": [
1852
- { "id": "title-2", "type": "SplitText", "props": { "content": "SlideTransition", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "center" } },
1853
- { "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 } }
1854
2012
  ]
1855
2013
  }
1856
2014
  },
1857
2015
  {
1858
2016
  "id": "s3",
1859
2017
  "durationInFrames": 90,
1860
- "transitionToNext": { "type": "WipeTransition", "durationInFrames": 15, "props": { "direction": "diagonal", "softEdge": true } },
2018
+ "transitionToNext": { "type": "WipeTransition", "duration": "base", "props": { "direction": "diagonal", "softEdge": true } },
1861
2019
  "root": {
1862
2020
  "id": "scene-3",
1863
2021
  "type": "Scene",
1864
- "props": { "background": { "type": "color", "value": "#111827" } },
2022
+ "props": { "background": { "type": "color", "token": "muted" } },
1865
2023
  "children": [
1866
- { "id": "title-3", "type": "SplitText", "props": { "content": "WipeTransition", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "scale", "position": "center" } },
1867
- { "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 } }
1868
2026
  ]
1869
2027
  }
1870
2028
  },
@@ -1874,10 +2032,10 @@ This example shows how to chain segments with overlaps using `transitionToNext`.
1874
2032
  "root": {
1875
2033
  "id": "scene-4",
1876
2034
  "type": "Scene",
1877
- "props": { "background": { "type": "color", "value": "#000000" } },
2035
+ "props": { "background": { "type": "color", "token": "background" } },
1878
2036
  "children": [
1879
- { "id": "title-4", "type": "SplitText", "props": { "content": "Done", "fontSize": 120, "splitBy": "letter", "stagger": 2, "animation": "rotate", "position": "center" } },
1880
- { "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" } }
1881
2039
  ]
1882
2040
  }
1883
2041
  }
@@ -1887,7 +2045,7 @@ This example shows how to chain segments with overlaps using `transitionToNext`.
1887
2045
 
1888
2046
  ### Example 3: data dashboard
1889
2047
 
1890
- File: `examples/data-dashboard.v2.json`
2048
+ File: `examples/data-dashboard.v3.json`
1891
2049
 
1892
2050
  This example uses data composites inside a `Grid`:
1893
2051
 
@@ -1898,8 +2056,9 @@ This example uses data composites inside a `Grid`:
1898
2056
 
1899
2057
  ```json
1900
2058
  {
1901
- "version": "2.0",
2059
+ "version": "3.0",
1902
2060
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 180 },
2061
+ "theme": { "preset": "studio-dark" },
1903
2062
  "segments": [
1904
2063
  {
1905
2064
  "id": "dashboard",
@@ -1907,14 +2066,14 @@ This example uses data composites inside a `Grid`:
1907
2066
  "root": {
1908
2067
  "id": "scene",
1909
2068
  "type": "Scene",
1910
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2069
+ "props": { "background": { "type": "color", "token": "surface" } },
1911
2070
  "children": [
1912
2071
  {
1913
2072
  "id": "grid",
1914
2073
  "type": "Grid",
1915
- "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" },
1916
2075
  "children": [
1917
- { "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 } },
1918
2077
  {
1919
2078
  "id": "bars",
1920
2079
  "type": "BarChart",
@@ -1923,10 +2082,10 @@ This example uses data composites inside a `Grid`:
1923
2082
  "showValues": true,
1924
2083
  "showGrid": true,
1925
2084
  "data": [
1926
- { "label": "A", "value": 32, "color": "#3B82F6" },
1927
- { "label": "B", "value": 56, "color": "#22C55E" },
1928
- { "label": "C", "value": 18, "color": "#F59E0B" },
1929
- { "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" }
1930
2089
  ]
1931
2090
  }
1932
2091
  },
@@ -1934,8 +2093,8 @@ This example uses data composites inside a `Grid`:
1934
2093
  "id": "line",
1935
2094
  "type": "LineGraph",
1936
2095
  "props": {
1937
- "color": "#60A5FA",
1938
- "strokeWidth": 4,
2096
+ "color": "primary",
2097
+ "strokeWidth": "2",
1939
2098
  "showDots": true,
1940
2099
  "fillArea": true,
1941
2100
  "animate": "draw",
@@ -1949,10 +2108,10 @@ This example uses data composites inside a `Grid`:
1949
2108
  ]
1950
2109
  }
1951
2110
  },
1952
- { "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 } }
1953
2112
  ]
1954
2113
  },
1955
- { "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" } }
1956
2115
  ]
1957
2116
  }
1958
2117
  }
@@ -1962,7 +2121,7 @@ This example uses data composites inside a `Grid`:
1962
2121
 
1963
2122
  ### Example 4: vertical social-style video (9:16)
1964
2123
 
1965
- File: `examples/social-vertical.v2.json`
2124
+ File: `examples/social-vertical.v3.json`
1966
2125
 
1967
2126
  This example uses:
1968
2127
 
@@ -1972,19 +2131,20 @@ This example uses:
1972
2131
 
1973
2132
  ```json
1974
2133
  {
1975
- "version": "2.0",
1976
- "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" },
1977
2137
  "segments": [
1978
2138
  {
1979
2139
  "id": "ig",
1980
2140
  "durationInFrames": 120,
1981
- "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 } } },
1982
2142
  "root": {
1983
2143
  "id": "ig-root",
1984
2144
  "type": "Scene",
1985
- "props": { "background": { "type": "color", "value": "#000000" } },
2145
+ "props": { "background": { "type": "color", "token": "background" } },
1986
2146
  "children": [
1987
- { "id": "ig", "type": "InstagramStory", "props": { "backgroundColor": "#0B1220", "username": "depths.ai", "text": "Waves v0.3.0 ships seamless alignment + overlays", "sticker": "poll" } }
2147
+ { "id": "ig", "type": "InstagramStory", "props": { "background": "background", "username": "depths.ai", "text": "Waves v0.5.0 improves robustness + performance", "sticker": "poll" } }
1988
2148
  ]
1989
2149
  }
1990
2150
  },
@@ -1994,10 +2154,10 @@ This example uses:
1994
2154
  "root": {
1995
2155
  "id": "tt-root",
1996
2156
  "type": "Scene",
1997
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2157
+ "props": { "background": { "type": "color", "token": "surface" } },
1998
2158
  "children": [
1999
- { "id": "caption", "type": "TikTokCaption", "props": { "text": "Composites make LLM video authoring dramatically easier", "position": "bottom", "highlightStyle": "bounce", "fontSize": 58, "strokeWidth": 4 } },
2000
- { "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" } }
2001
2161
  ]
2002
2162
  }
2003
2163
  }
@@ -2007,7 +2167,7 @@ This example uses:
2007
2167
 
2008
2168
  ### Example 5: intro -> stats -> outro (assets)
2009
2169
 
2010
- File: `examples/intro-stats-outro.v2.json`
2170
+ File: `examples/intro-stats-outro.v3.json`
2011
2171
 
2012
2172
  This example uses branding + layout + data composites:
2013
2173
 
@@ -2019,34 +2179,35 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
2019
2179
 
2020
2180
  ```json
2021
2181
  {
2022
- "version": "2.0",
2023
- "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" },
2024
2185
  "segments": [
2025
2186
  {
2026
2187
  "id": "intro",
2027
2188
  "durationInFrames": 120,
2028
- "transitionToNext": { "type": "FadeTransition", "durationInFrames": 15 },
2189
+ "transitionToNext": { "type": "FadeTransition", "duration": "base" },
2029
2190
  "root": {
2030
2191
  "id": "intro-scene",
2031
2192
  "type": "Scene",
2032
- "props": { "background": { "type": "color", "value": "#000000" } },
2193
+ "props": { "background": { "type": "color", "token": "background" } },
2033
2194
  "children": [
2034
- { "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.3.0", "backgroundColor": "#000000", "primaryColor": "#FFFFFF" } }
2195
+ { "id": "intro", "type": "IntroScene", "props": { "logoSrc": "/assets/logo.svg", "companyName": "Depths AI", "tagline": "Waves v0.5.0" } }
2035
2196
  ]
2036
2197
  }
2037
2198
  },
2038
2199
  {
2039
2200
  "id": "stats",
2040
2201
  "durationInFrames": 150,
2041
- "transitionToNext": { "type": "SlideTransition", "durationInFrames": 15, "props": { "direction": "up", "distance": 140 } },
2202
+ "transitionToNext": { "type": "SlideTransition", "duration": "base", "props": { "direction": "up", "distance": "travel" } },
2042
2203
  "root": {
2043
2204
  "id": "stats-scene",
2044
2205
  "type": "Scene",
2045
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2206
+ "props": { "background": { "type": "color", "token": "surface" } },
2046
2207
  "children": [
2047
- { "id": "lower-third", "type": "ThirdLowerBanner", "props": { "name": "Waves", "title": "Composite components", "accentColor": "#22C55E" } },
2048
- { "id": "counter", "type": "AnimatedCounter", "props": { "from": 0, "to": 44, "suffix": " types", "fontSize": 110, "color": "#FFFFFF", "animationType": "spring" } },
2049
- { "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" } ] } }
2050
2211
  ]
2051
2212
  }
2052
2213
  },
@@ -2056,7 +2217,7 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
2056
2217
  "root": {
2057
2218
  "id": "outro-scene",
2058
2219
  "type": "Scene",
2059
- "props": { "background": { "type": "color", "value": "#000000" } },
2220
+ "props": { "background": { "type": "color", "token": "background" } },
2060
2221
  "children": [
2061
2222
  {
2062
2223
  "id": "outro",
@@ -2064,18 +2225,11 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
2064
2225
  "props": {
2065
2226
  "logoSrc": "/assets/logo.svg",
2066
2227
  "message": "Thanks for watching",
2067
- "backgroundColor": "#000000",
2068
- "ctaButtons": [
2069
- { "text": "Star", "icon": "*" },
2070
- { "text": "Follow", "icon": "->" }
2071
- ],
2072
- "socialHandles": [
2073
- { "platform": "twitter", "handle": "@depths_ai" },
2074
- { "platform": "youtube", "handle": "@depths-ai" }
2075
- ]
2228
+ "ctaButtons": [ { "text": "Star", "icon": "*" }, { "text": "Follow", "icon": "->" } ],
2229
+ "socialHandles": [ { "platform": "twitter", "handle": "@depths_ai" }, { "platform": "youtube", "handle": "@depths-ai" } ]
2076
2230
  }
2077
2231
  },
2078
- { "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" } }
2079
2233
  ]
2080
2234
  }
2081
2235
  }
@@ -2085,7 +2239,7 @@ It references `/assets/logo.svg` style paths; supply those files and pass `--pub
2085
2239
 
2086
2240
  ### Example 6: timeline mode (global audio)
2087
2241
 
2088
- File: `examples/timeline-global-audio.v2.json`
2242
+ File: `examples/timeline-global-audio.v3.json`
2089
2243
 
2090
2244
  This example uses `timeline[]` to place a global `Audio` track as a root node spanning the entire video.
2091
2245
 
@@ -2093,17 +2247,18 @@ This is a common reason to choose timeline mode over segments mode: you want "gl
2093
2247
 
2094
2248
  ```json
2095
2249
  {
2096
- "version": "2.0",
2250
+ "version": "3.0",
2097
2251
  "video": { "id": "main", "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 150 },
2252
+ "theme": { "preset": "studio-dark" },
2098
2253
  "timeline": [
2099
2254
  {
2100
2255
  "id": "scene",
2101
2256
  "type": "Scene",
2102
2257
  "timing": { "from": 0, "durationInFrames": 150 },
2103
- "props": { "background": { "type": "color", "value": "#0B1220" } },
2258
+ "props": { "background": { "type": "color", "token": "surface" } },
2104
2259
  "children": [
2105
- { "id": "title", "type": "SplitText", "props": { "content": "Timeline mode", "fontSize": 96, "splitBy": "word", "stagger": 3, "animation": "slideUp", "position": "top" } },
2106
- { "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 } }
2107
2262
  ]
2108
2263
  },
2109
2264
  {
@@ -2123,8 +2278,9 @@ import { renderVideo } from '@depths/waves';
2123
2278
 
2124
2279
  await renderVideo(
2125
2280
  {
2126
- version: '2.0',
2281
+ version: '3.0',
2127
2282
  video: { id: 'main', width: 1920, height: 1080, fps: 30, durationInFrames: 60 },
2283
+ theme: { preset: 'studio-dark' },
2128
2284
  segments: [
2129
2285
  {
2130
2286
  id: 'scene-1',
@@ -2132,7 +2288,7 @@ await renderVideo(
2132
2288
  root: {
2133
2289
  id: 'root',
2134
2290
  type: 'Scene',
2135
- props: { background: { type: 'color', value: '#000000' } },
2291
+ props: { background: { type: 'color', token: 'background' } },
2136
2292
  children: [{ id: 't1', type: 'Text', props: { content: 'Hello' } }]
2137
2293
  }
2138
2294
  }
@@ -2142,7 +2298,7 @@ await renderVideo(
2142
2298
  );
2143
2299
  ```
2144
2300
 
2145
- ## Assets and paths (Windows/Linux)
2301
+ ## Assets and paths (Windows/Linux)
2146
2302
 
2147
2303
  In IR JSON, asset paths must be either:
2148
2304
 
@@ -2151,12 +2307,33 @@ In IR JSON, asset paths must be either:
2151
2307
 
2152
2308
  On Windows:
2153
2309
 
2154
- - Prefer forward slashes in IR (`/assets/foo.png`), not `C:\\...` paths.
2155
- - If you pass Windows paths to the CLI (e.g. `--out`), quote them if they contain spaces.
2156
-
2157
- ## Rendering prerequisites
2158
-
2159
- 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.
2160
2337
 
2161
2338
  ## Local CLI testing (before publishing)
2162
2339
 
@@ -2166,9 +2343,9 @@ From the `waves/` package directory:
2166
2343
  npm install
2167
2344
  npm run build
2168
2345
  node dist/cli.js --help
2169
- node dist/cli.js write-ir --template basic --pretty --out examples/basic.v2.json
2170
- node dist/cli.js validate --in examples/basic.v2.json
2171
- 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
2172
2349
  ```
2173
2350
 
2174
2351
  To test the installed experience:
@@ -2200,7 +2377,7 @@ The build emits ESM into `dist/`. Most CLI tests execute the built CLI via `node
2200
2377
 
2201
2378
  - `src/cli.ts`: the CLI (`waves <command> ...`)
2202
2379
  - `src/llm/prompt.ts`: system prompt + prompt payload (schemas + catalog)
2203
- - `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)
2204
2381
  - `src/ir/migrations.ts`: compiler (`compileToRenderGraph`) from authored IR -> render timeline
2205
2382
  - `src/core/registry.ts`: component registry + JSON Schema export for LLM use
2206
2383
  - `src/core/validator.ts`: schema + semantics + registry validation