Skip to content

feat(lint): flag dir="rtl" on <html> as a confirmed silent render failure#1893

Draft
miguel-heygen wants to merge 1 commit into
mainfrom
fix/lint-html-dir-breaks-render
Draft

feat(lint): flag dir="rtl" on <html> as a confirmed silent render failure#1893
miguel-heygen wants to merge 1 commit into
mainfrom
fix/lint-html-dir-breaks-render

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Summary

Two independent reports diagnosed the exact same bug: dir="rtl" (or any non-ltr value) on <html> renders correctly in preview/snapshot but produces a fully blank/black video from render, with no other lint/validate/inspect check catching it — output file size (far smaller than expected) was the only tell for both reporters.

"render (headless-shell) produces an all-black video when the composition sets dir="rtl" on <html>; snapshot/preview render the same Arabic composition correctly."

"CRITICAL: setting dir="rtl" on <html> for an Arabic/RTL variant renders correctly in preview/snapshot but produces a fully blank/white MP4 from 'render' at every timestamp ... This is a serious silent-failure gap for any i18n/RTL composition and deserves a lint rule or render-time warning."

Both independently confirmed the same fix: drop dir from <html>, keep lang, and scope direction: rtl to individual text-containing elements via CSS instead (text still bidi-shapes correctly through the browser's own algorithm).

Investigation notes (please read before assuming this is "fixed")

This PR is advisory only — it does not fix the underlying render-pipeline bug, because I could not empirically verify it in this session.

My working hypothesis: packages/engine/src/services/screenshotService.ts clips its Page.captureScreenshot CDP calls to a fixed {x: 0, y: 0, width, height} region, assuming the composition's content sits at the document's top-left origin. RTL layout can affect scroll-origin/viewport semantics in ways that could shift the effective content position away from (0,0) for absolutely-positioned elements, which would explain why a fixed top-left clip captures blank space while an interactive tab (snapshot/preview) doesn't hit the same issue.

I attempted to confirm this empirically with an isolated Puppeteer repro (bare RTL vs LTR page, same Page.captureScreenshot call as the engine) but headless Chrome CDP screenshot capture is unreliable in this sandboxed environment — even the LTR baseline call timed out on Page.captureScreenshot. I could not get a working comparison.

Given two independent, already-self-verified reports but no way to confirm the exact mechanism myself, I judged it safer to ship the confirmed advisory (both reporters explicitly asked for this) than to guess at a runtime fix to screenshotService.ts I couldn't test. Whoever picks up the actual render-pipeline fix should start with screenshotService.ts's clip region and verify against a real RTL composition in an environment where headless Chrome screenshot capture works reliably.

Fix (this PR)

New lint rule html_dir_attribute_breaks_render (error severity, matching the severity of other silent-default-breaking checks in this file) flags <html dir="..."> for any value other than ltr, with a fix hint naming the exact confirmed workaround.

Test plan

  • bunx vitest run packages/lint/src/rules/composition.test.ts — 98 tests pass (5 new)
  • bunx vitest run packages/lint/ — full package, 314 tests pass, no regressions
  • New tests: flags dir="rtl", flags any non-ltr value, does not flag dir="ltr", does not flag a missing dir attribute, does not flag the documented fix (element-scoped direction: rtl via CSS instead of the <html> attribute)

…lure

Two independent reports diagnosed the same exact bug: dir="rtl" (or any
non-ltr value) on <html> renders correctly in preview/snapshot but
produces a fully blank/black video from render, with no other
lint/validate/inspect check catching it - output file size (far smaller
than expected) was the only tell for both reporters. Both independently
confirmed the same fix: drop dir from <html>, keep lang, and scope
direction: rtl to individual text-containing elements via CSS instead.

Could not empirically verify the render pipeline's own root cause in this
session (headless Chrome screenshot capture is unreliable in this
sandboxed environment - even a baseline, non-RTL capture timed out), so
this ships the safe, already-confirmed advisory rather than guessing at a
runtime fix. Both reporters explicitly asked for exactly this: "deserves
a lint rule or render-time warning."
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