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:
- Input (
NGConstraintSpace): The parent element passes down an immutable set of constraints, such as available width, height, and writing mode. - Execution: The specific layout algorithm (Flexbox or Grid) runs using these constraints.
- 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-growfactor. - Negative Free Space: If the items overflow the
container, the engine calculates a "scaled flex shrink factor"
(multiplying the item's
flex-shrinkvalue 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 allfrunits in that axis. Eachfrtrack 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.