mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(cli): flag text with an effectively transparent fill in check
Wild report (5th in cluster, CLI 0.7.53): snapshots omitted all text while check passed — text painting with a transparent -webkit-text-fill-color (which overrides `color` for the glyph fill AND inherits, so a parent's transparent fill silently blanks descendant text that has its own opaque `color`) renders invisible, but every geometry/occlusion/contrast audit missed it. Contrast in particular reads `color`, not the fill that actually paints, so white-`color` + transparent-fill text scored as high-contrast and passed. Add an invisible-text detector to the layout audit: flag any text element whose effective fill (computed -webkit-text-fill-color, which already resolves to `color` when unset) is transparent. Gradient/clipped text (background-clip:text) legitimately uses a transparent fill and is excluded. Verified: check now fails on an inherited-transparent-fill fixture (text_not_painted) while gradient text, body-inherited color, sub-composition color, and real registry examples stay clean.
This commit is contained in:
@@ -796,6 +796,43 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Text whose glyphs paint with an effectively transparent fill renders
|
||||
// invisibly even though the element, its box, opacity and color all read as
|
||||
// present — so geometry/occlusion/contrast audits miss it (contrast reads
|
||||
// `color`, not the fill that actually paints). `-webkit-text-fill-color`
|
||||
// overrides `color` for the glyph fill AND inherits, so a parent's
|
||||
// `transparent` fill silently blanks descendant text that has its own opaque
|
||||
// `color`. Its computed value already resolves to `color` when unset, so it
|
||||
// is the effective fill directly. Gradient/clipped text (`background-clip:
|
||||
// text`) legitimately uses a transparent fill — the clipped background paints
|
||||
// the glyphs — so exclude it.
|
||||
function invisibleTextIssue(element, time) {
|
||||
const textRect = textRectFor(element);
|
||||
if (!textRect) return null;
|
||||
const text = textContentFor(element);
|
||||
if (!text) return null;
|
||||
const cs = getComputedStyle(element);
|
||||
const fill = cs.getPropertyValue("-webkit-text-fill-color") || cs.color;
|
||||
if (colorAlpha(fill) > 0.05) return null;
|
||||
const clip =
|
||||
cs.getPropertyValue("-webkit-background-clip") ||
|
||||
cs.getPropertyValue("background-clip") ||
|
||||
"";
|
||||
if (/text/i.test(clip)) return null;
|
||||
return {
|
||||
code: "text_not_painted",
|
||||
severity: "error",
|
||||
time,
|
||||
selector: selectorFor(element),
|
||||
text,
|
||||
message:
|
||||
"Text paints with an effectively transparent fill (-webkit-text-fill-color / color), so its glyphs are invisible.",
|
||||
rect: textRect,
|
||||
fixHint:
|
||||
"Set an explicit, opaque `color` on the text — and an explicit `-webkit-text-fill-color` if an ancestor makes the fill transparent. If the transparency is intentional gradient text, add `background-clip: text`.",
|
||||
};
|
||||
}
|
||||
|
||||
function candidateAnchor(element) {
|
||||
const dataAttributes = {};
|
||||
for (const attribute of Array.from(element.attributes)) {
|
||||
@@ -881,6 +918,8 @@
|
||||
issues.push(...textOverflowIssues(element, root, rootRect, time, tolerance));
|
||||
const occluded = occludedTextIssue(element, time);
|
||||
if (occluded) issues.push(occluded);
|
||||
const invisible = invisibleTextIssue(element, time);
|
||||
if (invisible) issues.push(invisible);
|
||||
}
|
||||
|
||||
issues.push(...containerOverflowIssues(root, time, tolerance));
|
||||
|
||||
@@ -946,6 +946,7 @@ const LAYOUT_ISSUE_CODES: readonly LayoutIssueCode[] = [
|
||||
"container_overflow",
|
||||
"content_overlap",
|
||||
"text_occluded",
|
||||
"text_not_painted",
|
||||
"caption_zone_collision",
|
||||
"frame_out_of_frame",
|
||||
"motion_appears_late",
|
||||
|
||||
@@ -16,6 +16,7 @@ export type LayoutIssueCode =
|
||||
| "container_overflow"
|
||||
| "content_overlap"
|
||||
| "text_occluded"
|
||||
| "text_not_painted"
|
||||
| "caption_zone_collision"
|
||||
| "frame_out_of_frame"
|
||||
// Frozen-sweep guard (#U10) — a whole-run meta-finding, not a per-sample
|
||||
|
||||
Reference in New Issue
Block a user