From 09bfe56589950aa4261116cee49d978a2b9fa369 Mon Sep 17 00:00:00 2001 From: SGavrl <277843151+SGavrl@users.noreply.github.com> Date: Fri, 31 Jul 2026 03:07:43 +0000 Subject: [PATCH] Keep positioned pseudos out of inline sizing --- crates/obscura-render/src/dom.rs | 32 +++++++++++++++++++++++++++++ crates/obscura-render/src/inline.rs | 15 +++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/crates/obscura-render/src/dom.rs b/crates/obscura-render/src/dom.rs index 065efd1..e8bad25 100644 --- a/crates/obscura-render/src/dom.rs +++ b/crates/obscura-render/src/dom.rs @@ -9435,6 +9435,38 @@ mod tests { assert_eq!(pseudo.line_height, host.line_height); } + #[test] + fn positioned_pseudo_does_not_wrap_shrink_to_fit_host_text() { + let tree = parse_html( + r#" + "#, + ); + let laid = layout_dom(&tree, (500.0, 300.0)); + let host = tree.get_element_by_id("host").unwrap(); + let rect = laid.rects[&host]; + + assert!( + (120.0..=130.0).contains(&rect.width), + "max-content width must include the shaped inter-word advance: {rect:?}" + ); + assert_eq!( + rect.height, 40.0, + "positioned generated content must not make the host label wrap" + ); + } + #[test] fn variable_font_properties_inherit_and_reset_through_elements_and_pseudos() { let tree = parse_html( diff --git a/crates/obscura-render/src/inline.rs b/crates/obscura-render/src/inline.rs index e83fbc8..1f84bcb 100644 --- a/crates/obscura-render/src/inline.rs +++ b/crates/obscura-render/src/inline.rs @@ -1568,7 +1568,20 @@ fn is_pure_text_ifc( let Some(style) = styles.get(&id) else { return false; }; - if style.before_pseudo.is_some() || style.after_pseudo.is_some() { + // Out-of-flow generated boxes neither contribute to nor interrupt the + // host's inline formatting context. Keeping a positioned decorative + // pseudo must not demote an otherwise text-only shrink-to-fit control to + // approximate per-word flex leaves: that path can disagree with shaped + // space advances and wrap text at its own max-content width. + let has_in_flow_pseudo = |pseudo: Option<&LayoutStyle>| { + pseudo.is_some_and(|pseudo| { + pseudo.display != Display::None + && !matches!(pseudo.position, Some(taffy::Position::Absolute)) + }) + }; + if has_in_flow_pseudo(style.before_pseudo.as_deref()) + || has_in_flow_pseudo(style.after_pseudo.as_deref()) + { return false; } // Only containers that lay their children out in normal flow (block, or