@@ -10,7 +10,7 @@ import { inlineExternalDefsAndSymbols } from '../modules/svgDefs.js'
1010import { cache } from '../core/cache.js'
1111import { freezeSticky } from '../modules/changeCSS.js'
1212import { resolveBlobUrlsInTree } from '../utils/clone.helpers.js'
13- import { stabilizeLayout } from '../utils/prepare.helpers.js'
13+ import { stabilizeLayout , forceContentVisibility } from '../utils/prepare.helpers.js'
1414
1515/**
1616 * Prepares a clone of an element for capture, inlining pseudo-elements and generating CSS classes.
@@ -37,6 +37,9 @@ export async function prepareClone(element, options = {}) {
3737
3838 stabilizeLayout ( element )
3939
40+ // #281: Force content-visibility:visible so Safari/Chromium don't skip offscreen elements
41+ const undoContentVisibility = forceContentVisibility ( element )
42+
4043 try {
4144 inlineExternalDefsAndSymbols ( element )
4245 } catch ( e ) {
@@ -48,6 +51,8 @@ export async function prepareClone(element, options = {}) {
4851 } catch ( e ) {
4952 console . warn ( 'deepClone failed:' , e )
5053 throw e
54+ } finally {
55+ undoContentVisibility ( )
5156 }
5257 try {
5358 await inlinePseudoElements ( element , clone , sessionCache , options )
@@ -108,6 +113,24 @@ export async function prepareClone(element, options = {}) {
108113 cloneNode . style . overflow = 'hidden'
109114 cloneNode . style . scrollbarWidth = 'none'
110115 cloneNode . style . msOverflowStyle = 'none'
116+
117+ // #364: Before wrapping with translate, adjust fixed/absolute descendants
118+ // so they don't shift when the translate wrapper creates a new containing block.
119+ try {
120+ const positioned = cloneNode . querySelectorAll ( '*' )
121+ for ( const child of positioned ) {
122+ if ( ! ( child instanceof HTMLElement ) ) continue
123+ const pos = child . style . position
124+ if ( pos === 'fixed' || pos === 'absolute' ) {
125+ const curTop = parseFloat ( child . style . top ) || 0
126+ const curLeft = parseFloat ( child . style . left ) || 0
127+ child . style . top = `${ curTop + scrollY } px`
128+ child . style . left = `${ curLeft + scrollX } px`
129+ if ( pos === 'fixed' ) child . style . position = 'absolute'
130+ }
131+ }
132+ } catch { /* non-blocking */ }
133+
111134 const inner = document . createElement ( 'div' )
112135 inner . style . transform = `translate(${ - scrollX } px, ${ - scrollY } px)`
113136 inner . style . willChange = 'transform'
0 commit comments