Skip to content

[css-grid-3][masonry] Handling baseline shim for multi-span items in intrinsic track sizing #13530

@yanlingwang23

Description

@yanlingwang23

From the CSS Grid Layout Module Level 3, track sizing can be optimized by aggregating items that have the same span size and placement into a single virtual item:

For each item group, if the items apply baseline alignment, determine the baselines of the virtual grid item by placing all of its items into a single hypothetical grid track and finding their shared baseline(s) and shims. Increase the group's intrinsic size contributions accordingly.

However, when dealing with multi-span items, this approach can lead to incorrect track sizes. Consider this example:

<div class="grid-lanes" style="display: grid-lanes; grid-lanes-direction: row; 
  width: 300px; grid-template-rows: minmax(0px, auto) minmax(0px, auto); 
  align-items: baseline; background: lightgray;"> 
  <div style="font-size: 20px; padding-top: 40px; width: 60px; background: plum; grid-row: 1 / 3;">É</div> 
  <div style="font-size: 20px; width: 60px; background: lightblue; grid-row: 1;">É</div> 
  <div style="font-size: 20px; width: 60px; background: lightgreen; grid-row: 2;">É</div> 
</div> 
grid-lanes grid
Image
demo
Image
demo

We observe that track sizes differ between grid and grid lanes—causing items to overflow their track in grid-lanes. In grid-lanes, both tracks are 37.5px, whereas in grid, track 1 is 75px and track 2 is 25px.

Root Cause Analysis

Given these item properties:

Item  Span size Item baseline Block Size Start Line End Line
Plum 2         71px          75px       0          2       
Blue 1         21px          25px       0          1       
Green 1         21px          25px       1          2       

In Grid: When calculating intrinsic size contributions, placement data is already available. The algorithm knows that both plum and blue items contribute to track 1's baseline. Therefore:

  • shared_baseline for track 1 = max(71, 21) = 71px
  • Blue: baseline_shim = 71 − 21 = 50px → block_contribution = 25 + 50 = 75px
  • Plum: baseline_shim = 71 − 71 = 0px → block_contribution = 75 + 0 = 75px
  • Result: Row 1 = 75px (correctly sized to accommodate both items' baselines)

In Grid-Lanes: We follow the spec's item grouping model without placement knowledge. Per the spec, items are grouped by span size. Since plum (2-row span) and blue (1-row span) have different spans, they end up in different item groups. Because shared_baseline is computed per-group:

  • Plum's group: shared_baseline = 71px, baseline_shim = 0px
  • Blue's group: shared_baseline = 21px, baseline_shim = 0px
  • Result: Blue's block_contribution = 25 + 0 = 25px, causing Row 1 to be undersized.
    Track 1 size = max(75/2 [Plum], 25 [Blue]) = 37.5px

Potential Solutions:

Option 1: Re-run track sizing

This approach would be to ignore baselines on the first track sizing pass, run placement, and then re-run track sizing once we know the baseline shims and placement. However, given that placement could change based on an update to track sizes, this approach could potentially be non-deterministic.

Option 2: Group by Baseline-Sharing Line with Overlap Analysis

  1. Group items by baseline-sharing line (start_line for first baseline, end_line for last baseline)
  2. Find overlapping groups based on possible placements
  3. Calculate max shared_baseline across overlapping groups and store in each item
  4. Group into actual virtual items and calculate contribution sizes

For explicitly placed items, we can skip step 2. Instead, group items by their baseline-sharing line regardless of span size, compute the correct shared_baseline for each line group, and then proceed to form virtual items for track sizing. For example, plum and blue both start at line 0 and are first baseline aligned, so they share the same baseline calculation; this yields the correct shared_baseline for blue of 71px (rather than 21px).

For implicitly placed items, the challenge is that the baseline-sharing line itself may vary with placement; overlap analysis is intended to capture the maximum shared_baseline across any groups that could end up sharing a track.

Consider a 3-row grid-lanes layout with align-items: last baseline and four items:

Item Placement
A    grid-row: span 2
B    grid-row: span 3
C    grid-row: 3
D    (auto-placed)

For shared baseline calculations:

  • B and C: Definitely are in the same “baseline sharing” group (both end at line 3)
  • A: Could end at line 2 or line 3 depending on placement, meaning it could have its own group and/or join B/C's group
  • D: Could be placed anywhere, so it could join A's group, B/C's group, or have its own

Given this, one would need to come up with every possible position of every item to get every possible combination of baseline sharing groups, which erodes the virtual item optimization to begin with.

Option 3: Group by Baseline Alignment Type Only

  1. Group items by baseline sharing groups: first-baseline aligned, last-baseline aligned
  2. Calculate shared_baseline within each category
  3. Group into actual virtual items and calculate contribution sizes

For instance, using the first example, since all three items are first-baseline aligned, they would end up in the same group in step 1. Their contribution sizes are calculated as follows:

  • shared_baseline for 3 items = max(71, 21, 21) = 71px
  • Blue: baseline_shim = 71 − 21 = 50px → block_contribution = 25 + 50 = 75px
  • Plum: baseline_shim = 71 − 71 = 0px → block_contribution = 75 + 0 = 75px
  • Green: baseline_shim = 71 − 21 = 50px → block_contribution = 25 + 50 = 75px
  • Result:
    • Row 1 = max(75/2 [Plum], 75 [Blue]) = 75px
    • Row 2 = max(75/2 [Plum], 75 [Green]) = 75px
    • Row 1 is correctly sized. However, Row 2 is over-expanded to 75px (instead of 25px in Grid) because Green's baseline shim is calculated against a shared baseline that includes Plum's 71px baseline. Per spec, Plum should only participate in baseline alignment for its start-most context (Row 1), not Row 2—but Solution 2's grouping doesn't account for this.

Based on current solutions, I'd prefer to proceed with option 3, given that the drawback of option 2 is that step 2 requires analyzing every possible combination of baseline groups for each item, which undermines the performance optimization that virtual items are intended to provide. However, both solutions may over-expand track sizes when there's a mix of span sizes and/or mixed auto/definite placement, though they do ensure tracks accommodate every possible item that could be placed in them.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions