From 6dec05f17177d77d93ecab2a5cf1dcab6bfda2c9 Mon Sep 17 00:00:00 2001 From: SGavrl <277843151+SGavrl@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:43:07 +0000 Subject: [PATCH] Respect auto margins on overflowing grid items --- crates/obscura-render/src/dom.rs | 51 ++++++++++++++ vendor/taffy/src/compute/grid/alignment.rs | 80 +++++++++++++++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index b34c5d7..df6a562 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -10345,6 +10345,57 @@ mod tests { } } + #[test] + fn overflowing_grid_item_auto_margins_use_safe_logical_start() { + let tree = parse_html( + r#" +
+
+
+
"#, + ); + let laid = layout_dom(&tree, (800.0, 300.0)); + let rect = |id: &str| laid.rects[&tree.get_element_by_id(id).unwrap()]; + + for (grid_id, item_id, expected_x) in [ + ("ltr-center-grid", "ltr-center", 0.0), + ("ltr-end-grid", "ltr-end", 0.0), + ] { + let grid = rect(grid_id); + let item = rect(item_id); + assert!( + (item.width - 600.0).abs() < 0.01, + "{item_id} lost its min-content floor: {item:?}" + ); + assert!( + (item.x - grid.x - expected_x).abs() < 0.01, + "{item_id} did not use safe logical-start overflow: grid={grid:?} item={item:?}" + ); + } + + for (grid_id, item_id, expected_x) in [ + ("absolute-center-grid", "absolute-center", -150.0), + ("absolute-end-grid", "absolute-end", -300.0), + ] { + let grid = rect(grid_id); + let item = rect(item_id); + assert!( + (item.x - grid.x - expected_x).abs() < 0.01, + "{item_id} incorrectly used the in-flow auto-margin fallback: grid={grid:?} item={item:?}" + ); + } + } + #[test] fn root_and_body_inline_outer_geometry_matches_blockification_rules() { for display in ["inline-block", "inline-flex", "inline-grid"] { diff --git a/vendor/taffy/src/compute/grid/alignment.rs b/vendor/taffy/src/compute/grid/alignment.rs index ae257f4..2a2af3f 100644 --- a/vendor/taffy/src/compute/grid/alignment.rs +++ b/vendor/taffy/src/compute/grid/alignment.rs @@ -370,7 +370,20 @@ pub(super) fn align_item_within_area( }; let overflows = resolved_size + non_auto_margin.sum() > grid_area_size; - let alignment_keyword = resolve_self_alignment_safety(alignment_style, overflows); + // In-flow auto margins take precedence over self-alignment and are always + // safe. When the item overflows there is no positive free space for the + // auto margins to absorb, so safe alignment falls back to the logical + // start edge rather than honoring an authored unsafe center/end alignment. + // Absolutely positioned items use their authored self-alignment here; + // their auto margins are resolved by the abs-pos constraint equation. + let alignment_keyword = if position != Position::Absolute + && auto_margin_count > 0 + && overflows + { + AlignItemsKeyword::Start + } else { + resolve_self_alignment_safety(alignment_style, overflows) + }; // Compute offset in the axis let alignment_based_offset = match alignment_keyword { @@ -612,4 +625,69 @@ mod tests { ); assert_eq!(content_box_max, 290.0); } + + #[test] + fn overflowing_auto_margins_override_unsafe_self_alignment() { + let area = Line { start: 0.0, end: 300.0 }; + let oversized = 600.0; + let margin_cases = [ + Line { start: None, end: Some(0.0) }, + Line { start: Some(0.0), end: None }, + Line { start: None, end: None }, + ]; + + for alignment in [AlignSelf::CENTER, AlignSelf::END] { + for margin in margin_cases { + let (ltr_start, ltr_margin) = super::align_item_within_area( + area, + alignment, + oversized, + Position::Relative, + Line { start: None, end: None }, + margin, + 0.0, + crate::Direction::Ltr, + ); + assert_eq!(ltr_start, 0.0); + assert_eq!(ltr_margin, Line { start: 0.0, end: 0.0 }); + + let (rtl_start, rtl_margin) = super::align_item_within_area( + area, + alignment, + oversized, + Position::Relative, + Line { start: None, end: None }, + margin, + 0.0, + crate::Direction::Rtl, + ); + assert_eq!(rtl_start, -300.0); + assert_eq!(rtl_margin, Line { start: 0.0, end: 0.0 }); + } + } + + let (absolute_center, _) = super::align_item_within_area( + area, + AlignSelf::CENTER, + oversized, + Position::Absolute, + Line { start: None, end: None }, + Line { start: None, end: Some(0.0) }, + 0.0, + crate::Direction::Ltr, + ); + assert_eq!(absolute_center, -150.0); + + let (absolute_end, _) = super::align_item_within_area( + area, + AlignSelf::END, + oversized, + Position::Absolute, + Line { start: None, end: None }, + Line { start: None, end: Some(0.0) }, + 0.0, + crate::Direction::Ltr, + ); + assert_eq!(absolute_end, -300.0); + } }