Arabic text: per-letter splits render isolated letters and word rows render reversed, with no lint warning · Issue #5885 · heygen-com/hyperframes · GitHub
Skip to content

Arabic text: per-letter splits render isolated letters and word rows render reversed, with no lint warning #5885

Open

Description

@AmroAlemam

Problem

Two common text recipes silently break Arabic (also Persian and Urdu; the second also affects Hebrew). Reproduced with hyperframes@0.8.169, both snapshot and render (chrome-headless-shell):

  1. Per-letter stagger (text.split("") into inline-block spans; SplitText chars does the same). Each letter is shaped alone, so "مرحبا بالعالم" renders as disconnected letters.
  2. Per-word stagger with no dir. Inline-block words are bidi-neutral, so the line defaults to LTR and the words come out in reverse order ("بالعالم مرحبا"). Flex phrase rows (asr-keyword-glow, caption rows) behave the same way.

hyperframes lint reports neither: 0 errors on the repro (re-checked on 0.8.173), only an unrelated structure warning. None of the 18 bundled fonts covers Arabic either, so it falls back to whatever the machine has.

Minimal repro (index.html; put NotoSansArabic.ttf from Google Fonts next to it):

<!doctype html>
<html lang="ar"><head><meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
  @font-face { font-family: "Noto Sans Arabic"; src: url("NotoSansArabic.ttf"); }
  body { margin: 0; background: #0b0f14; color: #fff; font-family: "Noto Sans Arabic", sans-serif; }
  #root { position: relative; width: 100%; height: 100%; }
  .clip { position: absolute; inset: 0; display: grid; place-content: center; gap: 40px; text-align: center; }
  .line { font-size: 96px; font-weight: 700; }
  .ch, .w { display: inline-block; }
</style></head>
<body>
<div id="root" data-composition-id="main" data-start="0" data-width="1280" data-height="720" data-duration="2">
  <section class="clip" data-start="0" data-duration="2">
    <div class="line" dir="rtl">مرحبا بالعالم</div>
    <div class="line" id="letters" dir="rtl"></div>
    <div class="line" id="words"></div>
  </section>
</div>
<script>
  const t = "مرحبا بالعالم";
  letters.innerHTML = t.split("").map((c) => `<span class="ch">${c === " " ? "&nbsp;" : c}</span>`).join("");
  words.innerHTML = t.split(" ").map((w) => `<span class="w">${w}</span>`).join(" ");
  const tl = gsap.timeline({ paused: true });
  tl.from("#letters .ch", { y: 40, opacity: 0, duration: 0.4, stagger: 0.04 }, 0);
  tl.from("#words .w", { y: 40, opacity: 0, duration: 0.4, stagger: 0.1 }, 0);
  window.__timelines["main"] = tl;
</script>
</body></html>

npx hyperframes render . -o out.mp4, last frame (row A reference, row B isolated letters, row C reversed words):

render-broken

Proposed solution

  • Lint warnings:
    • Arabic-script text split per character into separate elements;
    • inline-block or flex word rows holding RTL text with no dir or direction on the container;
    • Arabic text whose font-family has no Arabic coverage.
  • Skill guidance for agents. I have a draft rule, hyperframes-animation/rules/arabic-rtl-text.md, plus a one-line note in typography.md. It covers direction, word-level splits, a joiner-aware letter split, and fonts. I'll open it as a PR that references this issue.

Alternatives considered

Additional context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions