From fe231dc918063f98ec5c3af6028b1243742a039a Mon Sep 17 00:00:00 2001 From: "nico.burns" Date: Sat, 8 Aug 2026 00:28:42 +0000 Subject: [PATCH 1/2] Flexbox: fix automatic minimum size and used cross size for aspect-ratio items --- CHANGELOG.md | 4 ++ src/compute/flexbox.rs | 50 ++++++++++++++++--- ...min_height_auto_transferred_min_width.html | 17 +++++++ ..._transferred_min_width__border_box_ltr.xml | 13 +++++ ..._transferred_min_width__border_box_rtl.xml | 13 +++++ ...transferred_min_width__content_box_ltr.xml | 13 +++++ ...transferred_min_width__content_box_rtl.xml | 13 +++++ tests/xml/mod.rs | 20 ++++++++ 8 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 test_fixtures/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width.html create mode 100644 tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr.xml create mode 100644 tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl.xml create mode 100644 tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr.xml create mode 100644 tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7349d331..91cd0980dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ ### Fixed +- Flexbox: the content size suggestion used for the automatic minimum size of a flex item with an aspect ratio is now clamped by min/max cross sizes transferred through the aspect ratio, per [css-sizing-3 §5.1](https://www.w3.org/TR/css-sizing-3/#min-content-zero). Previously only the transferred max size was applied ([WPT: flexbox-min-height-auto-002b](https://wpt.live/css/css-flexbox/flexbox-min-height-auto-002b.html)) + +- Flexbox: the used cross size of a flex item whose cross size is transferred from its main size through its aspect ratio is now re-derived from the used (post-flexing) main size, instead of being transferred from the flex base size before flexing + - Flexbox: the static position of absolutely positioned children now resolves `justify-content: start`/`end` and `align-self: start`/`end`/`self-start`/`self-end` as writing-mode relative (flipping for RTL but not for `*-reverse` flex directions or `wrap-reverse`), whereas previously they were treated as flex-relative ([WPT: flex-abspos-staticpos-*](https://wpt.live/css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html)) - Flexbox: the static position of absolutely positioned children with `justify-content: space-between` (and the default `normal`) now follows the flex-relative start (matching `flex-start`), so it resolves to the correct edge in `row-reverse`/`column-reverse` containers diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index ce4d990f5b..9f04165d05 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -35,6 +35,11 @@ struct FlexItem { max_size: Size>, /// The aspect ratio of this item aspect_ratio: Option, + /// Whether the item's cross size is transferred from its main size through the aspect ratio + /// (i.e. the cross size property is `auto` and the item has an aspect ratio) + cross_size_is_transferred: bool, + /// The box sizing mode of this item + box_sizing: BoxSizing, /// The cross-alignment of this item align_self: AlignSelf, @@ -536,14 +541,14 @@ fn generate_anonymous_flex_items( let pb_sum = (padding + border).sum_axes(); let box_sizing_adjustment = if child_style.box_sizing() == BoxSizing::ContentBox { pb_sum } else { Size::ZERO }; + let raw_size = + child_style.size().maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis)); FlexItem { node: child, order: index as u32, - size: child_style - .size() - .maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis)) - .maybe_apply_aspect_ratio(aspect_ratio) - .maybe_add(box_sizing_adjustment), + size: raw_size.maybe_apply_aspect_ratio(aspect_ratio).maybe_add(box_sizing_adjustment), + cross_size_is_transferred: aspect_ratio.is_some() && raw_size.cross(constants.dir).is_none(), + box_sizing: child_style.box_sizing(), min_size: child_style .min_size() .maybe_resolve(constants.node_inner_size, |val, basis| tree.calc(val, basis)) @@ -848,8 +853,12 @@ fn determine_flex_base_size( // 4.5. Automatic Minimum Size of Flex Items // https://www.w3.org/TR/css-flexbox-1/#min-size-auto - let clamped_min_content_size = - min_content_main_size.maybe_min(child.size.main(dir)).maybe_min(transferred_max_size.main(dir)); + // If the item has an aspect ratio, the content size suggestion is clamped by + // min/max cross sizes transferred through the aspect ratio. + let clamped_min_content_size = min_content_main_size + .maybe_clamp(transferred_min_size.main(dir), transferred_max_size.main(dir)) + .maybe_min(child.size.main(dir)) + .maybe_min(transferred_max_size.main(dir)); clamped_min_content_size.maybe_max(padding_border_axes_sums.main(dir)) }); @@ -1677,6 +1686,33 @@ fn determine_used_cross_size( child.min_size.cross(constants.dir), max_size_ignoring_aspect_ratio.cross(constants.dir), ) + } else if child.cross_size_is_transferred { + // If the cross size is transferred from the main size through the aspect ratio + // then it is re-derived from the used (target) main size rather than taken from + // the hypothetical cross size, which was transferred from the flex base size + // before flexing. + let box_sizing_adjustment = if child.box_sizing == BoxSizing::ContentBox { + (child.padding + child.border).sum_axes() + } else { + Size::ZERO + }; + let transferred_min_cross = + child.min_size.maybe_apply_aspect_ratio(child.aspect_ratio).cross(constants.dir); + let transferred_max_cross = + child.max_size.maybe_apply_aspect_ratio(child.aspect_ratio).cross(constants.dir); + let padding_border_sum = + child.padding.cross_axis_sum(constants.dir) + child.border.cross_axis_sum(constants.dir); + Size::NONE + .with_main( + constants.dir, + Some(child.target_size.main(constants.dir) - box_sizing_adjustment.main(constants.dir)), + ) + .maybe_apply_aspect_ratio(child.aspect_ratio) + .maybe_add(box_sizing_adjustment) + .cross(constants.dir) + .maybe_clamp(transferred_min_cross, transferred_max_cross) + .unwrap_or(0.0) + .max(padding_border_sum) } else { child.hypothetical_inner_size.cross(constants.dir) }, diff --git a/test_fixtures/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width.html b/test_fixtures/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width.html new file mode 100644 index 0000000000..cdf7f61d21 --- /dev/null +++ b/test_fixtures/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr.xml b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr.xml new file mode 100644 index 0000000000..ad7455758a --- /dev/null +++ b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl.xml b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl.xml new file mode 100644 index 0000000000..e7c8fdb68f --- /dev/null +++ b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr.xml b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr.xml new file mode 100644 index 0000000000..256a2432f2 --- /dev/null +++ b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl.xml b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl.xml new file mode 100644 index 0000000000..2a17aa345a --- /dev/null +++ b/tests/xml/flex/aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/mod.rs b/tests/xml/mod.rs index 261179b6d3..d1fe0284f6 100644 --- a/tests/xml/mod.rs +++ b/tests/xml/mod.rs @@ -8911,6 +8911,26 @@ mod flex { crate::run_xml_test("flex", "aspect_ratio_flex_column_fill_width_flex__content_box_rtl"); } + #[test] + fn aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr() { + crate::run_xml_test("flex", "aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_ltr"); + } + + #[test] + fn aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr() { + crate::run_xml_test("flex", "aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_ltr"); + } + + #[test] + fn aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl() { + crate::run_xml_test("flex", "aspect_ratio_flex_column_min_height_auto_transferred_min_width__border_box_rtl"); + } + + #[test] + fn aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl() { + crate::run_xml_test("flex", "aspect_ratio_flex_column_min_height_auto_transferred_min_width__content_box_rtl"); + } + #[test] fn aspect_ratio_flex_column_stretch_fill_height__border_box_ltr() { crate::run_xml_test("flex", "aspect_ratio_flex_column_stretch_fill_height__border_box_ltr"); From 931d61e60d21d34d06adc440fafb370ced3a03f3 Mon Sep 17 00:00:00 2001 From: "nico.burns" Date: Sat, 8 Aug 2026 00:51:22 +0000 Subject: [PATCH 2/2] Block/float: shrink-to-fit size floated flex/grid containers; clamp cross available space by item margins --- CHANGELOG.md | 2 + src/compute/block.rs | 30 +++++++++++++- src/compute/flexbox.rs | 4 +- ...ntainer_max_width_margin_stretch_item.html | 19 +++++++++ .../float_flex_container_shrink_to_fit.html | 20 ++++++++++ ...th_margin_stretch_item__border_box_ltr.xml | 17 ++++++++ ...th_margin_stretch_item__border_box_rtl.xml | 17 ++++++++ ...h_margin_stretch_item__content_box_ltr.xml | 17 ++++++++ ...h_margin_stretch_item__content_box_rtl.xml | 17 ++++++++ ...ontainer_shrink_to_fit__border_box_ltr.xml | 19 +++++++++ ...ontainer_shrink_to_fit__border_box_rtl.xml | 19 +++++++++ ...ntainer_shrink_to_fit__content_box_ltr.xml | 19 +++++++++ ...ntainer_shrink_to_fit__content_box_rtl.xml | 19 +++++++++ tests/xml/mod.rs | 40 +++++++++++++++++++ 14 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 test_fixtures/float/float_flex_container_max_width_margin_stretch_item.html create mode 100644 test_fixtures/float/float_flex_container_shrink_to_fit.html create mode 100644 tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_ltr.xml create mode 100644 tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_rtl.xml create mode 100644 tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_ltr.xml create mode 100644 tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_rtl.xml create mode 100644 tests/xml/float/float_flex_container_shrink_to_fit__border_box_ltr.xml create mode 100644 tests/xml/float/float_flex_container_shrink_to_fit__border_box_rtl.xml create mode 100644 tests/xml/float/float_flex_container_shrink_to_fit__content_box_ltr.xml create mode 100644 tests/xml/float/float_flex_container_shrink_to_fit__content_box_rtl.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f6e377e8..4b2a1a46b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ A large number of miscelaneous bug fixes are included in this release: - Flexbox: clamp the content size suggestion used for the automatic minimum size of an aspect-ratio item by min/max cross sizes transferred through the aspect ratio, per [css-sizing-3 §5.1](https://www.w3.org/TR/css-sizing-3/#min-content-zero); previously only the transferred max size was applied - Flexbox: re-derive the used cross size of an item whose cross size is transferred from its main size through its aspect ratio from the used (post-flexing) main size, instead of transferring it from the flex base size before flexing +- Flexbox: clamp the cross-axis available space by the item's own cross-axis margins rather than the container's margins when sizing flex items; previously a container margin could inflate a stretched item's cross size beyond the container +- Block/float: floated flex and grid containers with `width: auto` are now shrink-to-fit (fit-content) sized; previously they treated the definite available space as stretch-fit - Flexbox: clamp the flex base size, automatic minimum size and hypothetical main/cross sizes with min/max sizes transferred through the aspect ratio, instead of baking them into the item's used min/max sizes (#989) - Flexbox: resolve `justify-content: start`/`end` and `align-self: start`/`end`/`self-start`/`self-end` as writing-mode relative (rather than flex-relative) in the static position of absolutely positioned children; use the flex-relative start for `justify-content: space-between`/`normal`, and a fallback of `start` for `align-self: baseline` (#1072) - Flexbox: only let `auto` margins on absolutely positioned children absorb free space when the box is inset-constrained in that axis; otherwise they resolve to zero per CSS2 §10.3.7/§10.6.4 (#1072) diff --git a/src/compute/block.rs b/src/compute/block.rs index 612747685b..37ec74a04f 100644 --- a/src/compute/block.rs +++ b/src/compute/block.rs @@ -951,11 +951,37 @@ fn perform_final_layout_on_in_flow_children( has_active_floats = true; // A float with `width: auto` is shrink-to-fit (fit-content) sized: the available - // space clamped between its min-content and max-content sizes. + // space clamped between its min-content and max-content sizes. Block containers + // implement this internally when laid out with definite available space and no + // known width, but other layout modes (e.g. a floated flex or grid container) + // treat definite available space as stretch-fit, so compute the fit-content + // width up front and pass it as a known dimension. let available_width = container_inner_width - item_non_auto_x_margin_sum; + let item_known_width = item.size.maybe_clamp(item.min_size, item.max_size).width.or_else(|| { + let min_content_width = tree.measure_child_size( + item.node_id, + Size::NONE, + parent_size, + Size { width: AvailableSpace::MinContent, height: AvailableSpace::MaxContent }, + SizingMode::InherentSize, + crate::AbsoluteAxis::Horizontal, + Line::FALSE, + ); + let max_content_width = tree.measure_child_size( + item.node_id, + Size::NONE, + parent_size, + Size { width: AvailableSpace::MaxContent, height: AvailableSpace::MaxContent }, + SizingMode::InherentSize, + crate::AbsoluteAxis::Horizontal, + Line::FALSE, + ); + Some(available_width.min(max_content_width).max(min_content_width)) + .maybe_clamp(item.min_size.width, item.max_size.width) + }); let item_layout = tree.perform_child_layout( item.node_id, - Size::NONE, + Size { width: item_known_width, height: None }, parent_size, Size { width: AvailableSpace::Definite(available_width), height: AvailableSpace::MaxContent }, SizingMode::InherentSize, diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index 9f04165d05..b7c9786afa 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -685,7 +685,7 @@ fn determine_flex_base_size( // Available space for child sizing // Min/max sizes transferred through the aspect ratio are taken into account here // https://github.com/w3c/csswg-drafts/issues/10997 - let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir); + let cross_axis_margin_sum = child.margin.cross_axis_sum(dir); let transferred_min_size = child.min_size.maybe_apply_aspect_ratio(child.aspect_ratio); let transferred_max_size = child.max_size.maybe_apply_aspect_ratio(child.aspect_ratio); let child_min_cross = transferred_min_size.cross(dir).maybe_add(cross_axis_margin_sum); @@ -1077,7 +1077,7 @@ fn determine_container_main_size( let cross_axis_parent_size = constants.node_inner_size.cross(dir); // Available space for child sizing - let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir); + let cross_axis_margin_sum = item.margin.cross_axis_sum(dir); let child_min_cross = item.min_size.cross(dir).maybe_add(cross_axis_margin_sum); let child_max_cross = item.max_size.cross(dir).maybe_add(cross_axis_margin_sum); let cross_axis_available_space: AvailableSpace = available_space diff --git a/test_fixtures/float/float_flex_container_max_width_margin_stretch_item.html b/test_fixtures/float/float_flex_container_max_width_margin_stretch_item.html new file mode 100644 index 0000000000..43068a1408 --- /dev/null +++ b/test_fixtures/float/float_flex_container_max_width_margin_stretch_item.html @@ -0,0 +1,19 @@ + + + + + + + Test description + + + + +
+
+
+
+
+ + + diff --git a/test_fixtures/float/float_flex_container_shrink_to_fit.html b/test_fixtures/float/float_flex_container_shrink_to_fit.html new file mode 100644 index 0000000000..5f515bf0e3 --- /dev/null +++ b/test_fixtures/float/float_flex_container_shrink_to_fit.html @@ -0,0 +1,20 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+ + + diff --git a/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_ltr.xml b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_ltr.xml new file mode 100644 index 0000000000..6c6d707fd0 --- /dev/null +++ b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_ltr.xml @@ -0,0 +1,17 @@ + + + +
+
+
+
+
+ + + + + + + + + diff --git a/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_rtl.xml b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_rtl.xml new file mode 100644 index 0000000000..f301fa1111 --- /dev/null +++ b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__border_box_rtl.xml @@ -0,0 +1,17 @@ + + + +
+
+
+
+
+ + + + + + + + + diff --git a/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_ltr.xml b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_ltr.xml new file mode 100644 index 0000000000..cc3ab9c0a9 --- /dev/null +++ b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_ltr.xml @@ -0,0 +1,17 @@ + + + +
+
+
+
+
+ + + + + + + + + diff --git a/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_rtl.xml b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_rtl.xml new file mode 100644 index 0000000000..40ab965147 --- /dev/null +++ b/tests/xml/float/float_flex_container_max_width_margin_stretch_item__content_box_rtl.xml @@ -0,0 +1,17 @@ + + + +
+
+
+
+
+ + + + + + + + + diff --git a/tests/xml/float/float_flex_container_shrink_to_fit__border_box_ltr.xml b/tests/xml/float/float_flex_container_shrink_to_fit__border_box_ltr.xml new file mode 100644 index 0000000000..24ae2634f3 --- /dev/null +++ b/tests/xml/float/float_flex_container_shrink_to_fit__border_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/float/float_flex_container_shrink_to_fit__border_box_rtl.xml b/tests/xml/float/float_flex_container_shrink_to_fit__border_box_rtl.xml new file mode 100644 index 0000000000..ce67964652 --- /dev/null +++ b/tests/xml/float/float_flex_container_shrink_to_fit__border_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/float/float_flex_container_shrink_to_fit__content_box_ltr.xml b/tests/xml/float/float_flex_container_shrink_to_fit__content_box_ltr.xml new file mode 100644 index 0000000000..171f48c742 --- /dev/null +++ b/tests/xml/float/float_flex_container_shrink_to_fit__content_box_ltr.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/float/float_flex_container_shrink_to_fit__content_box_rtl.xml b/tests/xml/float/float_flex_container_shrink_to_fit__content_box_rtl.xml new file mode 100644 index 0000000000..d3fe017799 --- /dev/null +++ b/tests/xml/float/float_flex_container_shrink_to_fit__content_box_rtl.xml @@ -0,0 +1,19 @@ + + + +
+
+
+
+
+
+ + + + + + + + + + diff --git a/tests/xml/mod.rs b/tests/xml/mod.rs index d1fe0284f6..79c4490a54 100644 --- a/tests/xml/mod.rs +++ b/tests/xml/mod.rs @@ -17339,6 +17339,46 @@ mod float { crate::run_xml_test("float", "float_clear_self_collapsing_margins__content_box_rtl"); } + #[test] + fn float_flex_container_max_width_margin_stretch_item__border_box_ltr() { + crate::run_xml_test("float", "float_flex_container_max_width_margin_stretch_item__border_box_ltr"); + } + + #[test] + fn float_flex_container_max_width_margin_stretch_item__content_box_ltr() { + crate::run_xml_test("float", "float_flex_container_max_width_margin_stretch_item__content_box_ltr"); + } + + #[test] + fn float_flex_container_max_width_margin_stretch_item__border_box_rtl() { + crate::run_xml_test("float", "float_flex_container_max_width_margin_stretch_item__border_box_rtl"); + } + + #[test] + fn float_flex_container_max_width_margin_stretch_item__content_box_rtl() { + crate::run_xml_test("float", "float_flex_container_max_width_margin_stretch_item__content_box_rtl"); + } + + #[test] + fn float_flex_container_shrink_to_fit__border_box_ltr() { + crate::run_xml_test("float", "float_flex_container_shrink_to_fit__border_box_ltr"); + } + + #[test] + fn float_flex_container_shrink_to_fit__content_box_ltr() { + crate::run_xml_test("float", "float_flex_container_shrink_to_fit__content_box_ltr"); + } + + #[test] + fn float_flex_container_shrink_to_fit__border_box_rtl() { + crate::run_xml_test("float", "float_flex_container_shrink_to_fit__border_box_rtl"); + } + + #[test] + fn float_flex_container_shrink_to_fit__content_box_rtl() { + crate::run_xml_test("float", "float_flex_container_shrink_to_fit__content_box_rtl"); + } + #[test] fn float_forced_clearance_adjoining_float__border_box_ltr() { crate::run_xml_test("float", "float_forced_clearance_adjoining_float__border_box_ltr");