Skip to content

Commit ea616f2

Browse files
committed
fix(layout): derive available grid space per CSS Grid §11.3 to restore indefinite fr resolution
1 parent e6c9368 commit ea616f2

3 files changed

Lines changed: 103 additions & 3 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Regression tests for CSS Grid §11.3 "available grid space".
2+
//
3+
// Per spec (https://www.w3.org/TR/css-grid-1/#algo-terms), independently
4+
// per dimension, the available grid space is:
5+
// - the container's content box, when the container has a definite size;
6+
// - the active min-/max-content constraint (indefinite), when the
7+
// container is being sized under such a constraint;
8+
// - indefinite, otherwise.
9+
//
10+
// When available grid space is indefinite, fr tracks must be resolved via
11+
// §11.7.1 ("Find the Size of an fr") using items' max-content contributions,
12+
// not the ancestor's available space.
13+
//
14+
// These tests lock down that the grid container never inherits the
15+
// ancestor's available space as its own definite available grid space
16+
// in `SizingMode::Normal`, and therefore the container collapses to the
17+
// children's contribution under indefinite sizing.
18+
19+
use crate::*;
20+
21+
// §11.3 branch: container size is indefinite (inline-grid, width auto),
22+
// parent provides available space but it MUST NOT be taken as definite
23+
// available grid space. fr tracks must resolve via §11.7.1.
24+
// Expected: container width = sum of children max-content (100 + 100 = 200).
25+
#[test]
26+
fn grid_available_space_indefinite_inline_grid_collapses_to_children() {
27+
assert_xml!(
28+
r#"
29+
<div style="display: inline-grid; grid-template-columns: 1fr 1fr;" expect_width="200">
30+
<div style="width: 100px; height: 50px;" expect_left="0" expect_width="100"></div>
31+
<div style="width: 100px; height: 50px;" expect_left="100" expect_width="100"></div>
32+
</div>
33+
"#,
34+
true
35+
)
36+
}
37+
38+
// §11.3 branch: container has a definite size, available grid space is
39+
// the content box. fr tracks resolve via the definite path and split it.
40+
#[test]
41+
fn grid_available_space_definite_uses_content_box() {
42+
assert_xml!(
43+
r#"
44+
<div style="display: grid; width: 300px; grid-template-columns: 1fr 1fr;">
45+
<div style="height: 50px;" expect_left="0" expect_width="150"></div>
46+
<div style="height: 50px;" expect_left="150" expect_width="150"></div>
47+
</div>
48+
"#,
49+
true
50+
)
51+
}
52+
53+
// §11.3 branch: indefinite container with unequal fr values still uses
54+
// §11.7.1 indefinite resolution (based on each child's max-content
55+
// contribution scaled by its fr): hyp_1fr = max(100/1, 150/2) = 100,
56+
// so track widths are 1fr=100, 2fr=200, and container width = 300.
57+
// This test asserts the container width only; item-in-track sizing
58+
// (§11.7/§11.8) is intentionally not asserted here.
59+
#[test]
60+
fn grid_available_space_indefinite_unequal_fr_uses_max_contribution() {
61+
assert_xml!(
62+
r#"
63+
<div style="display: inline-grid; grid-template-columns: 1fr 2fr;" expect_width="300">
64+
<div style="width: 100px; height: 50px;"></div>
65+
<div style="width: 150px; height: 50px;"></div>
66+
</div>
67+
"#,
68+
true
69+
)
70+
}
71+
72+
// Nested case: a block flow wraps the inline-grid. The outer ancestor has
73+
// a definite width (375 default viewport) and propagates available space
74+
// down; if that value leaks into the inner grid's available grid space,
75+
// fr tracks would wrongly split it. This test pins the regression fixed
76+
// in grid/mod.rs: in Normal sizing mode, `request.max_content` of the
77+
// inner grid MUST NOT be used as its available grid space.
78+
#[test]
79+
fn grid_available_space_indefinite_ignores_ancestor_available_space() {
80+
assert_xml!(
81+
r#"
82+
<div>
83+
<div style="display: inline-grid; grid-template-columns: 1fr 1fr;" expect_width="200">
84+
<div style="width: 100px; height: 50px;" expect_left="0" expect_width="100"></div>
85+
<div style="width: 100px; height: 50px;" expect_left="100" expect_width="100"></div>
86+
</div>
87+
</div>
88+
"#,
89+
true
90+
)
91+
}

float-pigment-forest/tests/custom/css_grid/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// - Grid items are placed into cells according to the grid auto-placement algorithm
66
// - `auto` track sizing allows tracks to size based on content
77

8+
mod available_grid_space;
89
mod gap;
910

1011
use crate::*;

float-pigment-layout/src/algo/grid/mod.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,18 @@ impl<T: LayoutTreeNode> GridContainer<T> for LayoutUnit<T> {
164164
requested_size.width - padding_border.horizontal(),
165165
requested_size.height - padding_border.vertical(),
166166
));
167-
167+
// CSS Grid §11.3 "available grid space" (per dimension):
168+
// - Definite container size => use its content box.
169+
// - Under a min-/max-content constraint => use that constraint
170+
// (still indefinite).
171+
// - Otherwise => indefinite.
172+
let available_grid_space_source = match request.sizing_mode {
173+
SizingMode::Normal => *requested_size,
174+
SizingMode::MinContent | SizingMode::MaxContent => *request.max_content,
175+
};
168176
let mut available_grid_space = OptionSize::new(
169-
requested_size.width.or(request.max_content.width) - padding_border.horizontal(),
170-
requested_size.height.or(request.max_content.height) - padding_border.vertical(),
177+
available_grid_space_source.width - padding_border.horizontal(),
178+
available_grid_space_source.height - padding_border.vertical(),
171179
);
172180

173181
// ═══════════════════════════════════════════════════════════════════════

0 commit comments

Comments
 (0)