mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 15:16:35 +03:00
fix: address PR review bot findings on background resolution
- Treat a url() image layer stacked above a gradient as an occluding, unreadable surface: resolveBackgroundInfo now returns unresolved so the gradient-stop fallback never measures stops the image hides (greptile-apps finding, reproduced in Chrome). - Route the glow and AI-palette DOM adapters through resolveBackgroundInfo so an unresolved surface makes them abstain instead of hunting gradient ancestors past an unreadable layer (Cursor Bugbot finding). - Resolve background-color keywords jsdom hands through verbatim: inherit now reads as no-paint (the ancestor walk IS its resolution) and currentcolor substitutes the element's own computed text color instead of forcing an abstention (Copilot finding). - Regression coverage in the dark-theme fixture for all three, asserted in both the jsdom and real-Chrome suites; browser detector regenerated. AI-assisted: prepared with Claude Code at the maintainer's direction. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1736,6 +1736,15 @@ function resolveBackgroundInfo(el, win, customPropMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// `background-color: currentcolor` paints with the element's own text
|
||||
// color — real paint whose value we know. Real browsers resolve the
|
||||
// keyword before getComputedStyle output; jsdom hands it through
|
||||
// verbatim, and without this substitution the layer would read as
|
||||
// unparseable and force a needless abstention.
|
||||
if ((!bg || bg.a < 0.1) && /^currentcolor$/i.test(String(style.backgroundColor || '').trim())) {
|
||||
bg = parseRgb(style.color) || parseAnyColor(style.color);
|
||||
}
|
||||
|
||||
if (bg && bg.a > 0.1) {
|
||||
if (bg.a >= 0.99) return { color: flatten(bg), unresolved: false };
|
||||
overlays.push(bg);
|
||||
@@ -1747,10 +1756,27 @@ function resolveBackgroundInfo(el, win, customPropMap) {
|
||||
// reporting an ancestor the visitor never sees.
|
||||
return { color: null, unresolved: true };
|
||||
}
|
||||
// No solid bg-color at this level. A gradient or image at THIS level is
|
||||
// the surface: hand the caller a null color so it falls back to the
|
||||
// gradient's own stops (body grounds, gradient buttons, hero sections).
|
||||
if (hasGradientOrUrl) return { color: null, unresolved: false };
|
||||
// No solid bg-color at this level, but this level paints an image. CSS
|
||||
// stacks background-image layers first-on-top, so which layer leads
|
||||
// decides what the visitor sees:
|
||||
// • gradient on top — the gradient is the surface. Hand the caller a
|
||||
// null color so it falls back to the gradient's own stops (body
|
||||
// grounds, gradient buttons, hero sections).
|
||||
// • url() on top — the surface is an image whose pixels this engine
|
||||
// cannot read, and it may fully cover every layer and ancestor
|
||||
// beneath it. Same contract as an unparseable color: abstain, so
|
||||
// the gradient-stop fallback never measures a gradient the image
|
||||
// hides (the shipped miss: `url(photo), linear-gradient(...)`
|
||||
// reported low-contrast against the invisible gradient's stops).
|
||||
if (hasGradientOrUrl) {
|
||||
const topPaintLayer = splitTopLevelCommas(bgImage).find(
|
||||
(layer) => /gradient\s*\(/i.test(layer) || /url\s*\(/i.test(layer),
|
||||
);
|
||||
const gradientOnTop = !!topPaintLayer
|
||||
&& /gradient\s*\(/i.test(topPaintLayer)
|
||||
&& !/^\s*url\s*\(/i.test(topPaintLayer);
|
||||
return { color: null, unresolved: !gradientOnTop };
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
// Every layer up to the document root was genuinely see-through, so the
|
||||
@@ -2544,7 +2570,12 @@ function checkElementGlowDOM(el) {
|
||||
if (!boxShadow && !textShadow) return [];
|
||||
// Use parent's background — glow radiates outward, so the surrounding context matters
|
||||
// If resolveBackground returns null (gradient), try to infer from the gradient colors
|
||||
let parentBg = el.parentElement ? resolveBackground(el.parentElement) : resolveBackground(el);
|
||||
const parentBgInfo = resolveBackgroundInfo(el.parentElement || el);
|
||||
// Unknown surface (an unreadable layer on the way up): abstain. The
|
||||
// gradient hunt below would walk PAST that layer and score the glow
|
||||
// against a background the visitor never sees.
|
||||
if (parentBgInfo.unresolved) return [];
|
||||
let parentBg = parentBgInfo.color;
|
||||
if (!parentBg) {
|
||||
// Gradient background — sample its colors to determine if it's dark
|
||||
let cur = el.parentElement;
|
||||
@@ -2594,10 +2625,13 @@ function checkElementAIPaletteDOM(el) {
|
||||
const hue = getHue(textColor);
|
||||
const isAIPalette = (hue >= 160 && hue <= 200) || (hue >= 260 && hue <= 310);
|
||||
if (isAIPalette) {
|
||||
const parentBg = el.parentElement ? resolveBackground(el.parentElement) : null;
|
||||
// Also check gradient parents
|
||||
let effectiveBg = parentBg;
|
||||
if (!effectiveBg) {
|
||||
const parentBgInfo = el.parentElement
|
||||
? resolveBackgroundInfo(el.parentElement)
|
||||
: { color: null, unresolved: false };
|
||||
// Unknown surface: leave effectiveBg null (no finding) rather than
|
||||
// hunting gradient ancestors past a layer we could not read.
|
||||
let effectiveBg = parentBgInfo.color;
|
||||
if (!effectiveBg && !parentBgInfo.unresolved) {
|
||||
let cur = el.parentElement;
|
||||
while (cur && cur.nodeType === 1) {
|
||||
const gi = getComputedStyle(cur).backgroundImage || '';
|
||||
|
||||
Reference in New Issue
Block a user