How Chromium Processes CSS Grid and Flexbox in LayoutNG

This article explores how the Chromium web browser calculates and renders CSS Grid and Flexbox layouts using its modern rendering engine, Blink, and its architectural upgrade, LayoutNG. It breaks down the core rendering pipeline, the creation of immutable fragment trees, and the specific mathematical passes required to resolve flexible lengths and complex two-dimensional grid tracks efficiently.

The LayoutNG Architecture

Chromium's Blink rendering engine relies on a layout architecture called LayoutNG (Next Generation Layout). Before calculating Flexbox or Grid specifics, Blink converts the Document Object Model (DOM) and computed CSS styles into an internal LayoutNG tree.

LayoutNG operates on a strict functional pipeline:

  1. Input (NGConstraintSpace): The parent element passes down an immutable set of constraints, such as available width, height, and writing mode.
  2. Execution: The specific layout algorithm (Flexbox or Grid) runs using these constraints.
  3. Output (NGPhysicalFragment): The algorithm produces an immutable tree of fragments representing the final pixel sizes and physical coordinates of the elements.

The Flexbox Calculation Pipeline

When Chromium encounters a display: flex container, it invokes the NGFlexLayoutAlgorithm. Because Flexbox is one-dimensional, calculations focus on resolving one axis before the other in sequential phases.

1. Line Construction and Base Sizing The engine first determines the main axis (row or column). It iterates through each flex item to calculate its "flex base size" (derived from the flex-basis property or the item's intrinsic content size). It then clamps this value between the item's min-width/min-height and max-width/max-height to establish the "hypothetical main size." If flex-wrap is enabled, Chromium groups items into multiple flex lines whenever the cumulative hypothetical sizes exceed the container's NGConstraintSpace.

2. Resolving Flexible Lengths For each flex line, Chromium subtracts the sum of the items' hypothetical main sizes from the container's available space to find the remaining "free space."

  • Positive Free Space: The engine distributes the extra pixels among items based on their flex-grow factor.
  • Negative Free Space: If the items overflow the container, the engine calculates a "scaled flex shrink factor" (multiplying the item's flex-shrink value by its inner flex base size) and shrinks the items proportionally so they fit the container.

3. Cross-Axis Sizing With the main axis dimensions locked, Chromium performs a layout pass on the children to determine their cross-axis sizes (for example, calculating the height of items in a row-based flex container). If an item has align-self: stretch, its cross-axis size is forced to match the tallest item in that specific flex line.

4. Alignment and Fragment Generation Finally, the engine resolves alignment properties (justify-content, align-items, and align-content). It calculates the exact X and Y offsets for each item relative to the flex container and outputs the final NGPhysicalFragment for each child.

The CSS Grid Calculation Pipeline

CSS Grid (display: grid) is processed by the NGGridLayoutAlgorithm (often referred to as GridNG). Because Grid is two-dimensional, Chromium must calculate the structure of the grid container's tracks (rows and columns) before it can determine the final size of the grid items.

1. Grid Placement Chromium begins by building an internal representation of the grid matrix. It first places items that have explicit coordinates (defined via grid-row and grid-column). Next, it runs an auto-placement cursor through the matrix to slot remaining items into the next available empty cells, expanding the implicit grid tracks as necessary.

2. The Track Sizing Algorithm Resolving the pixel sizes of rows and columns is the most computationally intensive part of the Grid engine. Chromium processes columns first, then rows, using a four-step sub-algorithm:

  • Initialize Track Sizes: Fixed tracks (pixels, percentages) are resolved immediately to definite values. Intrinsic tracks (min-content, max-content, auto) have their base sizes initialized to zero and their growth limits set to infinity.
  • Resolve Intrinsic Track Sizes: Chromium measures the actual content of the grid items. It first resolves items that span only a single track, updating the track's base size to fit the item. Next, it evaluates items spanning multiple tracks, distributing the required extra space evenly across the spanned intrinsic tracks.
  • Maximize Tracks: If free space remains in the grid container, the engine expands the base sizes of the tracks up to their calculated growth limits.
  • Expand Flexible (fr) Tracks: Any leftover free space is divided by the sum of all fr units in that axis. Each fr track is then multiplied by this quotient to receive its final pixel allocation.

3. Item Layout and Alignment Once all column and row tracks have definite pixel dimensions, Chromium calculates the exact bounding box (Grid Area) for each item. The engine constructs a new NGConstraintSpace matching the exact dimensions of that Grid Area and passes it to the grid item. The item lays itself out within those bounds, and Chromium applies justify-self and align-self offsets before generating the final physical fragments.

Performance Optimization via Caching

Both Flexbox and Grid frequently require measuring child elements multiple times to figure out their min-content and max-content sizes before finalizing the layout. In legacy browser engines, nesting Flexbox or Grid containers caused exponential layout passes, resulting in severe slowdowns.

Chromium mitigates this through LayoutNG's immutability. Whenever a flex or grid item is measured during an early sizing phase, Blink caches the resulting NGLayoutResult alongside the NGConstraintSpace that produced it. When the engine reaches the final layout pass, if the calculated constraints for that item have not changed, Chromium bypasses the layout algorithm for that child entirely and reuses the cached fragment. This architecture ensures that complex, deeply nested Grid and Flexbox layouts render at high frame rates.