Skip to content

Inherited CSS color invisible in rendered output (Page.captureScreenshot path) #1290

@Guomingze

Description

@Guomingze

Describe the bug

Text elements that inherit color from a parent (via CSS inheritance) are invisible in the rendered MP4 output, even though they display correctly in hyperframes preview. Explicitly setting color on the element itself resolves the issue.

This affects any composition where text relies on CSS color inheritance rather than an explicit inline color: declaration. The bug occurs on the pageScreenshotCapture render path (macOS/Windows), which uses Page.captureScreenshot CDP.

Link to reproduction

I cannot share the full repo publicly. I can create a minimal reproduction project if needed.

Steps to reproduce

  1. Create a composition with a root element that sets color: #e8e8e8
  2. Add a child text element that does NOT declare color explicitly (relies on inheritance)
  3. Animate the text with a GSAP tl.from() or tl.fromTo() tween
  4. Run npx hyperframes render
  5. The inherited-color text is invisible in the output

Minimal example:

<!-- index.html -->
<div id="root" data-composition-id="main" data-start="0" data-duration="10"
     style="width:1920px;height:1080px;background:#0a0a0a;color:#e8e8e8">
  <div class="clip" data-composition-id="s1" data-composition-src="compositions/s1.html"
       data-start="0" data-duration="10" style="position:absolute;inset:0;z-index:1">
  </div>
</div>
<!-- compositions/s1.html -->
<template id="s1-template">
  <div data-composition-id="s1" style="width:1920px;height:1080px;color:#e8e8e8;background:#0a0a0a">
    <div id="title" style="position:absolute;left:92px;top:100px;font-size:76px;font-weight:950">
      This text is invisible in render
    </div>
    <div id="subtitle" style="position:absolute;left:24px;bottom:16px;color:#67e8f9;font-size:32px">
      This text is visible in render
    </div>
  </div>
</template>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({paused: true});
tl.from('#title', {opacity: 0, y: 34, duration: .55, ease: 'power3.out'}, .1);
window.__timelines.s1 = tl;
</script>

Expected behavior

Text inheriting color from a parent element should be visible in the rendered MP4, matching the preview.

Actual behavior

Inherited-color text is invisible in the rendered output. Explicit color on the element fixes it.

Diagnostic steps taken:

  1. Text IS in the DOM and positioned correctly — visible with background:rgba(0,255,0,0.5) + color:#ff0000
  2. Removed all GSAP tweens → still invisible (rules out GSAP animation bug)
  3. Removed Three.js entirely → still invisible (rules out WebGL compositing)
  4. Added explicit color:#e8e8e8text becomes visible

Root cause hypothesis:

GSAP's tl.from() uses immediateRender: true, which sets inline opacity:0; transform:translateY(34px) on the element at timeline creation. When the animation completes and GSAP cleans up these inline styles, the element returns to a bare state relying on CSS inheritance for color. On Chrome's Page.captureScreenshot CDP path, the compositor does not properly resolve the inherited color value. Only color inheritance is affected — other inherited properties like font-family resolve correctly.

Environment

✓ Version          0.6.71
✓ Node.js          v22.x.x (darwin arm64)
✓ CPU              10 cores · Apple M2 Pro @ 2400MHz
✓ OS               macOS arm64
✓ Chrome           system: Google Chrome 148.0.7778.216
✗ Docker           Not found

Additional context

The relevant screenshot code is in packages/engine/src/services/screenshotService.ts, pageScreenshotCapture():

await client.send("Page.captureScreenshot", {
  format: isPng ? "png" : "jpeg",
  fromSurface: true,
  captureBeyondViewport: true,
  clip: { x: 0, y: 0, width: options.width, height: options.height, scale: dpr },
});

The BeginFrame path (HeadlessExperimental.beginFrame) is Linux-only and may or may not exhibit the same issue.

Workaround: Add explicit color to every text element in your compositions instead of relying on inheritance.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions