Skip to content

fix(video_compose): derive the Remotion theme from real playbook keys - #474

Open
bbudaedu wants to merge 1 commit into
calesthio:mainfrom
bbudaedu:fix/remotion-theme-playbook-keys
Open

fix(video_compose): derive the Remotion theme from real playbook keys#474
bbudaedu wants to merge 1 commit into
calesthio:mainfrom
bbudaedu:fix/remotion-theme-playbook-keys

Conversation

@bbudaedu

@bbudaedu bbudaedu commented Aug 5, 2026

Copy link
Copy Markdown

The defect

VideoCompose._build_theme_from_playbook documents itself as:

Derive a Remotion ThemeConfig from a playbook's actual color values. Instead of passing a playbook name and hoping Remotion has a matching preset, we read the playbook YAML and extract concrete colors/fonts.

Three of the keys it reads are not in schemas/styles/playbook.schema.json, so each silently fell back to a hardcoded default for every playbook:

Read Schema key Consequence
typo.get("heading") headings every playbook rendered its headings in Inter
palette.get("muted_text") muted mutedTextColor was always #6B7280
motion.get("pace") identity.pace no playbook's declared pace ever reached the spring config

Before → after:

flat-motion-graphics  headingFont  Inter    -> Space Grotesk
                      muted        #6B7280  -> #64748B
                      spring       20/120 @0.4s -> 12/80 @0.3s   (pace: fast)
minimalist-diagram    headingFont  Inter    -> IBM Plex Sans
anime-ghibli          headingFont  Inter    -> Noto Serif JP

This is the Remotion counterpart of the HyperFrames style-bridge defect in #306. That bridge lives in lib/hyperframes_style_bridge.py and is untouched here, as are the open PRs against it (#296, #307).

Note on ordering

Root.tsx's resolveTheme currently prefers a named preset over themeConfig, and four of the five playbook names collide with Remotion preset names. So the derived theme was being discarded exactly where these defaults would have shown — which masked this bug.

Fixing resolveTheme first would have made every video lose its heading typeface, muted color and motion pacing in one step. This PR has to land first.

Deliberately left alone

  • palette.get("surface", bg) also names a key the schema does not define, but the fallback to the background color is an explicit, sensible default rather than a dropped value. Giving playbooks a real surface color would be a schema addition, not a bug fix.
  • The pace branch still handles only fast and slow, so deliberate and rapid land on the moderate default. Extending that mapping is a motion tuning decision, not part of reading the right key.

Coverage

tests/contracts/test_remotion_theme_playbook_keys.py takes its expectations from the playbook YAML rather than a hardcoded table, so a playbook that changes its typeface keeps the test honest rather than making it stale.

Verification

  • 4 cases fail on the unfixed tree; all pass after.
  • Full suite: 964 → 977 passed, 13 skipped, no regressions.
  • make lint: passed.

The 3 skips are anime-ghibli, which cannot yield a theme on current main for the reason #468 fixes; they start running once that lands.

Independent of #468, #469, #472 and #473 — no overlapping files.

🤖 Generated with Claude Code

`_build_theme_from_playbook` documents itself as reading "a playbook's actual
color values [...] not picked from a preset menu", but three of the keys it
reads are not in the playbook schema, so each silently fell back to a
hardcoded default for every playbook:

    typo.get("heading")        schema key is `headings`
    palette.get("muted_text")  schema key is `muted`
    motion.get("pace")         `pace` is an identity field; motion carries
                               pacing_rules, not a pace enum

Effect, before -> after:

    flat-motion-graphics  headingFont  Inter -> Space Grotesk
                          muted        #6B7280 -> #64748B
                          spring       20/120 @0.4s -> 12/80 @0.3s  (pace: fast)
    minimalist-diagram    headingFont  Inter -> IBM Plex Sans
    anime-ghibli          headingFont  Inter -> Noto Serif JP

Every playbook rendered its headings in Inter regardless of what it declared,
and no playbook's declared pace ever reached the spring config.

This is the Remotion counterpart of the HyperFrames style-bridge defect in
issue calesthio#306. That bridge lives in lib/hyperframes_style_bridge.py and is
untouched here, as are the open PRs against it (calesthio#296, calesthio#307).

Two adjacent things deliberately left alone:

- `palette.get("surface", bg)` also names a key the schema does not define,
  but the fallback to the background color is an explicit, sensible default
  rather than a dropped value. Giving playbooks a real surface color would be
  a schema addition, not a bug fix.
- The pace branch still handles only `fast` and `slow`, so `deliberate` and
  `rapid` land on the moderate default. Extending that mapping is a motion
  tuning decision, not part of reading the right key.

Note on ordering: Root.tsx's resolveTheme currently prefers a named preset
over themeConfig, which meant this derived theme was discarded whenever the
playbook name matched a Remotion preset — masking the defect. Fixing
resolveTheme before this would have made every video lose its heading
typeface, muted color and motion pacing at once.

Expectations in the new tests come from the playbook YAML rather than a
hardcoded table, so a playbook that changes its typeface keeps them honest.

Verified: 4 cases fail on the unfixed tree; full suite goes 964 -> 977 passed
with no regressions.
@bbudaedu
bbudaedu requested a review from calesthio as a code owner August 5, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant