Skip to content

Commit 7fe0a3f

Browse files
committed
Fix margin collapsing in some cases. See #243
1 parent afe5ff8 commit 7fe0a3f

1 file changed

Lines changed: 95 additions & 0 deletions

File tree

src/modules/styles.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ function getSnapshot(el, preStyle = null, options = {}) {
7474
if (rec && rec.epoch === __epoch) return rec.snapshot;
7575
const style = preStyle || getComputedStyle(el);
7676
const snap = snapshotComputedStyleFull(style, options);
77+
stripHeightForWrappers(el, style, snap);
7778
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
7879
return snap;
7980
}
@@ -148,3 +149,97 @@ export async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
148149
}
149150
session.styleMap.set(clone, key);
150151
}
152+
/**
153+
* @param {Element} el
154+
* @returns {boolean}
155+
*/
156+
function isReplaced(el) {
157+
return el instanceof HTMLImageElement ||
158+
el instanceof HTMLCanvasElement ||
159+
el instanceof HTMLVideoElement ||
160+
el instanceof HTMLIFrameElement ||
161+
el instanceof SVGElement ||
162+
el instanceof HTMLObjectElement ||
163+
el instanceof HTMLEmbedElement;
164+
}
165+
166+
/**
167+
* Caja “visual”: bg/border/padding u overflow ≠ visible.
168+
* @param {CSSStyleDeclaration} cs
169+
*/
170+
function hasBox(cs) {
171+
if (cs.backgroundImage && cs.backgroundImage !== 'none') return true;
172+
if (cs.backgroundColor && cs.backgroundColor !== 'rgba(0, 0, 0, 0)' && cs.backgroundColor !== 'transparent') return true;
173+
if ((parseFloat(cs.borderTopWidth) || 0) > 0) return true;
174+
if ((parseFloat(cs.borderBottomWidth) || 0) > 0) return true;
175+
if ((parseFloat(cs.paddingTop) || 0) > 0) return true;
176+
if ((parseFloat(cs.paddingBottom) || 0) > 0) return true;
177+
const ob = cs.overflowBlock || cs.overflowY || 'visible';
178+
return ob !== 'visible';
179+
}
180+
181+
/**
182+
* Item de flex/grid (mirando display del padre, 1 getComputedStyle).
183+
* @param {Element} el
184+
*/
185+
function isFlexOrGridItem(el) {
186+
const p = el.parentElement;
187+
if (!p) return false;
188+
const pd = getComputedStyle(p).display || '';
189+
return pd.includes('flex') || pd.includes('grid');
190+
}
191+
192+
/**
193+
* ¿Hay contenido en flujo? Versión rápida:
194+
* - Texto no vacío → true (no dispara layout).
195+
* - <br> inmediato → true.
196+
* - Geometry probe: scrollHeight > padding (abspos NO suma) → true.
197+
* @param {Element} el
198+
* @param {CSSStyleDeclaration} cs // ya lo tenemos en mano
199+
*/
200+
function hasFlowFast(el, cs) {
201+
if (el.textContent && /\S/.test(el.textContent)) return true;
202+
const f = el.firstElementChild, l = el.lastElementChild;
203+
if ((f && f.tagName === 'BR') || (l && l.tagName === 'BR')) return true;
204+
205+
// Probe geométrico (1 lectura de layout): evita recorrer hijos
206+
// Nota: scrollHeight no incluye hijos absolute; si sólo hay absolute → ≈ padding
207+
const sh = el.scrollHeight;
208+
if (sh === 0) return false;
209+
const pt = parseFloat(cs.paddingTop) || 0;
210+
const pb = parseFloat(cs.paddingBottom) || 0;
211+
return sh > pt + pb;
212+
}
213+
214+
/**
215+
* Quita height/block-size SOLO en wrappers transparentes de flujo normal
216+
* que SÍ tienen contenido en flujo. Mantiene height si no hay flujo (sólo abspos),
217+
* si el autor lo puso inline, si es replaced, posicionado/transform, tiene caja visual,
218+
* o es item flex/grid.
219+
* @param {Element} el
220+
* @param {CSSStyleDeclaration} cs
221+
* @param {Record<string,string>} snap
222+
*/
223+
function stripHeightForWrappers(el, cs, snap) {
224+
// autor inline → respetar
225+
if (el instanceof HTMLElement && el.style && el.style.height) return;
226+
227+
// ⛳️ clave para Orbit: si EL ELEMENTO es contenedor flex/grid, no tocar su height
228+
const disp = cs.display || '';
229+
if (disp.includes('flex') || disp.includes('grid')) return;
230+
231+
// guardas existentes
232+
if (isReplaced(el)) return;
233+
const pos = cs.position;
234+
if (pos === 'absolute' || pos === 'fixed' || pos === 'sticky') return;
235+
if (cs.transform !== 'none') return;
236+
if (hasBox(cs)) return;
237+
if (isFlexOrGridItem(el)) return;
238+
239+
// wrapper transparente con flujo → permitir margin-collapsing
240+
if (!hasFlowFast(el, cs)) return;
241+
242+
delete snap.height;
243+
delete snap['block-size'];
244+
}
245+

0 commit comments

Comments
 (0)