You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
check passes when an overflow: hidden text mask cuts descenders in the resting state #5231
The common line-mask reveal (text rises inside an overflow: hidden wrapper) leaves the wrapper as tall as the line box. With a tight line-height (display type is usually set at 0.9 to 1.1), the descenders of g/p/y/j and the umlaut or accent marks above capitals sit outside the line box, so the mask cuts them after the animation has finished, for the rest of the shot. hyperframes check reports nothing at the settled samples.
Two things combine:
Since fix(layout): flag text a clipping card cuts off #4577 the clipping-card text check uses line-height bounds "to avoid treating the font's full ascent/descent as clipped display text". That avoids false positives, but it also means real descender ink outside the line box is never compared with the clipping ancestor.
The sanctioned waiver for parked content is data-layout-allow-overflow on the mask (hyperframes-animation rules and blueprints recommend it for exactly this pattern). It is all-or-nothing over the timeline, so it also waives the resting state. In our real case the mask carried the waiver, check passed, three agent self-reviews of contact sheets missed it, and the client spotted the cut "g" in a screenshot.
At t = 2, 5, 9 the reveal has long finished. The tail of the "g" is cut by #name-mask, so check should report a clipped-text finding (e.g. text_box_overflow / clipped_text against #name-mask) at least at warning severity.
Actual behavior
check returns "ok": true with errorCount 0, warningCount 0, infoCount 0 for layout. The zoomed snapshot clearly shows the "g" descender cut off flat at the mask edge. During the entrance (t ≈ 0.56) check emits only info-level text_box_overflow/container_overflow, which is the expected transient.
A Range-rect probe (document.createRange().selectNodeContents(textNode).getBoundingClientRect(), i.e. font ascent..descent) against every ancestor with overflow != visible flags it at all settled times: glyph box 422..657 px vs mask 440..640 px. Padding the mask (padding-block: 0.2em; margin-block: -0.2em) makes the probe pass at settled times and only fail mid-entrance, which is correct.
Suggested fixes, any of which would have caught it:
Check: at samples where the subject is not moving relative to its clipping ancestor (settled), compare font ascent/descent (or measured ink bounds, which fix(layout): flag text a clipping card cuts off #4577 already computes for parked lines) with the clipping ancestor and report a cut. Keep the line-height exemption for moving/parked content only.
Waiver scope: let data-layout-allow-overflow be limited to motion, e.g. data-layout-allow-overflow="transit", so the resting state is still audited; or document that the waiver hides resting clipping.
Skills: in hyperframes-animation/techniques.md §12 (Clip-Path Reveal Masks) and the mask-reveal-up guidance, state that a text mask needs block padding of about 0.2em (descenders, umlauts, accents) with a compensating negative margin, or must release the clip (overflow: visible / clip-path: none) once the entrance settles.
Duplicate search (issues and PRs, open and closed): descender, descenders, glyph, "text clipping", "clipped text overflow", "mask reveal", "overflow hidden text", "ink bounds", text_occluded, "clip text check". Related but not covering this: #4577 (merged, see above), #2767 (closed, scrollHeight > clientHeight truncation, which does not fire because the line box fits), #3923 (closed, content_overlap trimming), #5207 (open, gradient text masks).
Describe the bug
The common line-mask reveal (text rises inside an
overflow: hiddenwrapper) leaves the wrapper as tall as the line box. With a tightline-height(display type is usually set at 0.9 to 1.1), the descenders of g/p/y/j and the umlaut or accent marks above capitals sit outside the line box, so the mask cuts them after the animation has finished, for the rest of the shot.hyperframes checkreports nothing at the settled samples.Two things combine:
data-layout-allow-overflowon the mask (hyperframes-animationrules and blueprints recommend it for exactly this pattern). It is all-or-nothing over the timeline, so it also waives the resting state. In our real case the mask carried the waiver,checkpassed, three agent self-reviews of contact sheets missed it, and the client spotted the cut "g" in a screenshot.Minimal reproduction
Then replace the
#titlerule, the<h1>and the example tween inrepro/index.html:(No
data-layout-allow-overflowhere, so this is the strictest case.)Steps to reproduce
cd repro && npx hyperframes check --json --at 2,5,9npx hyperframes snapshot --at 5 --no-end --zoom "#name-mask"Expected behavior
At t = 2, 5, 9 the reveal has long finished. The tail of the "g" is cut by
#name-mask, socheckshould report a clipped-text finding (e.g.text_box_overflow/clipped_textagainst#name-mask) at least at warning severity.Actual behavior
checkreturns"ok": truewitherrorCount 0, warningCount 0, infoCount 0for layout. The zoomed snapshot clearly shows the "g" descender cut off flat at the mask edge. During the entrance (t ≈ 0.56)checkemits only info-leveltext_box_overflow/container_overflow, which is the expected transient.A Range-rect probe (
document.createRange().selectNodeContents(textNode).getBoundingClientRect(), i.e. font ascent..descent) against every ancestor withoverflow != visibleflags it at all settled times: glyph box 422..657 px vs mask 440..640 px. Padding the mask (padding-block: 0.2em; margin-block: -0.2em) makes the probe pass at settled times and only fail mid-entrance, which is correct.Environment
Additional context
Suggested fixes, any of which would have caught it:
data-layout-allow-overflowbe limited to motion, e.g.data-layout-allow-overflow="transit", so the resting state is still audited; or document that the waiver hides resting clipping.hyperframes-animation/techniques.md§12 (Clip-Path Reveal Masks) and themask-reveal-upguidance, state that a text mask needs block padding of about 0.2em (descenders, umlauts, accents) with a compensating negative margin, or must release the clip (overflow: visible/clip-path: none) once the entrance settles.Duplicate search (issues and PRs, open and closed): descender, descenders, glyph, "text clipping", "clipped text overflow", "mask reveal", "overflow hidden text", "ink bounds", text_occluded, "clip text check". Related but not covering this: #4577 (merged, see above), #2767 (closed,
scrollHeight > clientHeighttruncation, which does not fire because the line box fits), #3923 (closed,content_overlaptrimming), #5207 (open, gradient text masks).