From b630b14fb64b0452f2d685fedad9099dc0a24681 Mon Sep 17 00:00:00 2001 From: SGavrl <277843151+SGavrl@users.noreply.github.com> Date: Fri, 31 Jul 2026 04:07:59 +0000 Subject: [PATCH] Keep min-height-contained floats at block start --- crates/obscura-render/src/dom.rs | 22 +++++++++++---- crates/obscura-render/tests/layout_test.rs | 33 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index c7a13fe..a0b1694 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -7958,7 +7958,11 @@ fn build_children_with_float_zone( && has_in_flow_generated_pseudo(Some(after)) }) }); - if parent_has_definite_height || parent_has_clearfix { + let parent_min_height = styles.get(&parent_id).and_then(|parent| match parent.min_height { + crate::Dimension::Px(value) => Some(value), + _ => None, + }); + if parent_has_definite_height || parent_has_clearfix || parent_min_height.is_some() { let fills_axis = |value: f32| (value - 1.0).abs() < 0.001; let substantive: Vec = dom_children .iter() @@ -7985,12 +7989,20 @@ fn build_children_with_float_zone( matches!(style.height, crate::Dimension::Percent(value) if fills_axis(value)) && style.size_expressions[1].is_none() }); - let float_has_definite_height = float_style.is_some_and(|style| { - matches!(style.height, crate::Dimension::Px(_)) && style.size_expressions[1].is_none() - }) || float_fills_height; + let float_pixel_height = float_style.and_then(|style| match style.height { + crate::Dimension::Px(value) if style.size_expressions[1].is_none() => Some(value), + _ => None, + }); + let float_has_definite_height = float_pixel_height.is_some() || float_fills_height; + let height_is_already_contained = parent_has_definite_height + || parent_has_clearfix + || parent_min_height + .zip(float_pixel_height) + .is_some_and(|(minimum, height)| minimum + 0.001 >= height); let sole_full_width_float = substantive.len() == 1 && float_percent_width.is_some_and(fills_axis) - && float_has_definite_height; + && float_has_definite_height + && height_is_already_contained; let split_band_flow = if parent_has_definite_height && substantive.len() == 2 && substantive[0] == float_dom diff --git a/crates/obscura-render/tests/layout_test.rs b/crates/obscura-render/tests/layout_test.rs index 3bbd004..e92f770 100644 --- a/crates/obscura-render/tests/layout_test.rs +++ b/crates/obscura-render/tests/layout_test.rs @@ -978,6 +978,39 @@ fn clearfix_contains_a_full_width_definite_height_float() { ); } +#[test] +fn min_height_floor_keeps_a_full_width_float_at_block_start() { + let tree = parse_html( + r#" + +
+
+ "#, + ); + let layout = layout_dom(&tree, (800.0, 700.0)); + let rect = |id| layout.rects[&tree.get_element_by_id(id).unwrap()]; + let host = rect("host"); + let float = rect("float"); + let following = rect("following"); + assert!( + (host.width - 800.0).abs() < 0.01 && (host.height - 600.0).abs() < 0.01, + "host: {host:?}" + ); + assert!( + (float.x - 0.0).abs() < 0.01 + && (float.y - 0.0).abs() < 0.01 + && (float.width - 800.0).abs() < 0.01 + && (float.height - 600.0).abs() < 0.01, + "float: {float:?}" + ); + assert!((following.y - 600.0).abs() < 0.01, "following: {following:?}"); +} + #[test] fn percentage_float_and_inline_sibling_share_definite_height_band() { let tree = parse_html(