mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
fix(live): resolve canvas background from ancestors when element is transparent
Screenshotting a transparent container rendered black because we were no longer passing `backgroundColor` to modern-screenshot at all (to avoid its `background-color !important` override on elements with their own bg, like the teal card). That fix left elements without their own bg rendering on a transparent canvas, which reads as black wherever the PNG is previewed. Now we resolve per-element: if the element has an opaque background-color or a background-image, omit the option (element's own bg renders, no override). If it's transparent, walk up ancestors to the first opaque background (falling back to body/html) and pass that as the canvas fill. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
1ba75a820e
commit
101dc50362
@@ -1767,6 +1767,41 @@
|
||||
return inlineFontUrls(chunks.join('\n'));
|
||||
}
|
||||
|
||||
// True if `s` is a computed color string that renders as nothing
|
||||
// (explicit `transparent`, or `rgba(...)` with alpha 0).
|
||||
function isTransparentColor(s) {
|
||||
if (!s) return true;
|
||||
if (s === 'transparent') return true;
|
||||
const m = /rgba?\(([^)]+)\)/.exec(s);
|
||||
if (!m) return false;
|
||||
const parts = m[1].split(',').map((p) => p.trim());
|
||||
if (parts.length === 4) return parseFloat(parts[3]) === 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// modern-screenshot force-sets `background-color: X !important` on the
|
||||
// cloned root whenever `backgroundColor` is passed, clobbering the
|
||||
// element's own background. So we only pass it when the element is
|
||||
// genuinely transparent (no own color, no own image) — in that case
|
||||
// we resolve up the DOM to the nearest opaque ancestor so the capture
|
||||
// sits on the page's real background instead of rendering black.
|
||||
function resolveCanvasBackground(el) {
|
||||
const own = getComputedStyle(el);
|
||||
if (!isTransparentColor(own.backgroundColor)) return null;
|
||||
if (own.backgroundImage && own.backgroundImage !== 'none') return null;
|
||||
let node = el.parentElement;
|
||||
while (node) {
|
||||
const cs = getComputedStyle(node);
|
||||
if (!isTransparentColor(cs.backgroundColor)) return cs.backgroundColor;
|
||||
node = node.parentElement;
|
||||
}
|
||||
return (
|
||||
getComputedStyle(document.body).backgroundColor ||
|
||||
getComputedStyle(document.documentElement).backgroundColor ||
|
||||
'#ffffff'
|
||||
);
|
||||
}
|
||||
|
||||
// Capture the element (with current annotations baked in) and return a PNG
|
||||
// Blob. Shared between the Go flow (uploads it to the server) and the
|
||||
// debug toggle (displays it as an overlay for side-by-side comparison).
|
||||
@@ -1787,14 +1822,11 @@
|
||||
try {
|
||||
const ms = await loadModernScreenshot();
|
||||
const fontCssText = await collectFontCssText();
|
||||
// Deliberately no `backgroundColor` option. modern-screenshot force-sets
|
||||
// `background-color: X !important` on the root clone's inline style when
|
||||
// that option is passed, clobbering the element's real background. Leave
|
||||
// the canvas transparent; the element's own background renders into the
|
||||
// foreignObject.
|
||||
const backgroundColor = resolveCanvasBackground(el);
|
||||
return await ms.domToBlob(el, {
|
||||
scale: Math.min(window.devicePixelRatio || 1, 2),
|
||||
font: fontCssText ? { cssText: fontCssText } : undefined,
|
||||
...(backgroundColor ? { backgroundColor } : {}),
|
||||
});
|
||||
} finally {
|
||||
if (annotNode) annotNode.remove();
|
||||
|
||||
Reference in New Issue
Block a user