Web Platform · CSS Layout

Grid Track Sizing and the fr Unit

How the grid algorithm sizes tracks from intrinsic contributions, why 1fr has an automatic minimum, and how auto-placement fills the implicit grid.

grid-template-columns: 1fr 1fr does not always produce two equal columns. When one column contains a long unbreakable word or an image with a large intrinsic width, that column takes the minimum width its content requires and the other column receives the rest. The reason is that an fr track is shorthand for minmax(auto, <flex factor>fr): the flexible factor governs how leftover space is distributed, while an auto minimum lets the content set a floor.

That single expansion explains most grid surprises. The rest come from the track sizing algorithm’s order of operations, which resolves intrinsic contributions before distributing free space, and from auto-placement, which decides which implicit tracks exist before sizing begins.

Defining the explicit grid

grid-template-columns and grid-template-rows define explicit tracks. Each track size can be a length, a percentage, a flex factor, min-content, max-content, auto, fit-content(<length>), or a minmax() pair. Tracks may also be named with bracketed line names, and names can repeat inside a repeat().

repeat() accepts a positive integer count or the keywords auto-fill and auto-fit. With auto-fill, the browser creates as many tracks as fit in the container without overflowing, leaving empty tracks if there are fewer items. With auto-fit, tracks that receive no items collapse to zero and the remaining tracks absorb the space. Both require a definite container size in the relevant axis for the count to be computed, and both are usually written with minmax() so that the track count responds to the available width without a media query.

Line numbering starts at 1 at the start edge. A negative line number counts from the end, so -1 is the last line, which makes grid-column: 1 / -1 a way to span every explicit column. A span can be specified instead of an end line: grid-column: 2 / span 3 starts at line 2 and covers three tracks. If both ends are specified as spans, the end span is ignored and the start determines the placement.

The sizing algorithm in order

The track sizing algorithm proceeds in phases, and knowing the phases is what makes fr predictable.

First, each track’s base size is initialised from its fixed component: a length track starts at that length, an fr track at zero, and an auto or intrinsic track at zero before content is considered. Each track also has a growth limit, which for a fixed track equals its base and for a flexible track is infinite.

Second, content contributions are resolved for tracks whose minimum depends on content. Each item contributes a minimum contribution and a maximum contribution to every track it spans. For a single-span item, the minimum contribution is the item’s min-content size in the relevant axis and the maximum contribution is its max-content size. For an item spanning several tracks, the algorithm distributes the contribution across the spanned tracks, preferring tracks with intrinsic sizing. Tracks are then grown from their base sizes toward their growth limits.

Third, the maximise tracks phase distributes any remaining free space equally among the tracks, freezing each one as it reaches its growth limit and continuing with the rest. Equal distribution, not proportional distribution, is what the specification asks for, so two tracks with different base sizes and the same growth limit each receive the same increment.

Fourth, flexible tracks are expanded. Only after the non-flexible tracks have their final sizes is the leftover space computed; it is then divided in proportion to the fr factors, and each flexible track’s size is set to its share. If the leftover space is negative, flexible tracks keep their base sizes and the grid overflows.

Fifth, tracks with an auto maximum are stretched to fill any space left over by align-content or justify-content being stretch.

The order explains the common failure: a 1fr track with a min-content floor of 300 pixels inside a 500-pixel grid leaves 200 pixels for the other 1fr track. The first track is not flexible below 300, so the sharing is not equal. Replacing 1fr with minmax(0, 1fr) removes the floor and restores equality, at the cost of allowing content to overflow its track.

/* Two equal columns that may shrink below min-content. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.5rem;
}

/* Responsive card grid without a media query. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

fr and indefinite space

An fr factor distributes free space. If the grid container’s size in the relevant axis is indefinite, there may be no free space to distribute, and the track’s size falls back to its base size, which for an fr track with an automatic minimum is its content-based minimum. A grid inside an auto-height block, with grid-template-rows: 1fr 1fr, therefore tends to size rows to their content rather than splitting the container in half, because the container has no definite height to split.

The same principle applies to percentages: a percentage track size resolves against the grid container’s content box in that axis, and is treated as auto when the container’s size depends on its contents. The definite-size rules that govern block percentage heights apply here too, with the grid container’s own sizing determining whether a percentage can resolve.

The fr unit also interacts with gaps. Gaps are subtracted from the available space before the flexible tracks divide what remains. A container of 1000 pixels with four columns and three 20-pixel gaps gives 940 pixels to the columns, so four equal 1fr tracks are 235 pixels each, not 250.

The implicit grid and auto-placement

Items placed without explicit line positions are auto-placed into cells by the grid auto-placement algorithm. The algorithm fills the explicit grid first, in row-major order by default (grid-auto-flow: row), advancing a cursor and skipping occupied cells. Items with a definite row or column position are placed before fully automatic items, and items with definite positions in both axes are placed first of all.

Placement can create tracks outside the explicit grid. Those tracks are the implicit grid, sized by grid-auto-rows and grid-auto-columns, which default to auto. An implicit track sized auto grows to fit its content, in contrast to an explicit 1fr track. Mixing explicit and implicit tracks is therefore a frequent source of rows that are the wrong height: an item placed on row 5 of a grid with three explicit rows lands in implicit rows sized auto.

grid-auto-flow: row dense allows the algorithm to backtrack and fill earlier holes with later, smaller items. The default sparse packing never moves the cursor backwards, so a wide item can leave a gap that a later narrow item does not fill. Dense packing changes visual order relative to source order, which matters for keyboard navigation and reading order; the algorithm does not reorder the DOM, only the placement.

Items with a span can be auto-placed as well. grid-column: span 2 on an auto-placed item makes the algorithm search for a position where two adjacent cells are free, and if none exists before the end of the row, the item is pushed to the next row and the row is extended if necessary.

Spanning items and contribution distribution

An item covering several tracks does not simply add its size to each. Its contributions are distributed among the spanned tracks, with the specification defining how space is apportioned when the spanned tracks have different sizing functions. Tracks with intrinsic sizing receive space first, and the distribution is biased toward tracks that can still grow.

A practical consequence is that a spanning item with a large min-content size can force all the tracks it crosses to grow, which shifts the layout of unrelated items in the same columns. Placing the item in a single track, or giving the spanning item min-width: 0, constrains the effect.

Subgrid and inherited tracks

grid-template-columns: subgrid makes a grid item’s tracks align with the tracks of its parent grid rather than defining new ones. The item’s own items then participate in the parent’s track sizing, so a card’s internal fields can line up with the fields of a sibling card without duplicated track definitions.

A subgrid inherits the parent’s line names within the spanned range, and its gutters default to normal, which means they match the parent’s; a different gap is compensated with margins on the items so that the subgrid’s gutters still centre on the parent’s. Its content contributions still feed the parent’s sizing, so a subgrid can affect the parent’s track sizes; the algorithm treats the subgrid’s items as contributions to the parent’s tracks. The feature makes nested alignment possible without fixed track sizes, which is the problem the older technique of duplicating grid-template-columns on every card could not solve.

Track sizing and the flex algorithm solve similar problems with different orders of operations. The flex algorithm resolves base sizes first and then applies flexibility along one axis, as described in Flex Basis, Free Space, and the Grow-Shrink Algorithm; grid resolves intrinsic contributions across two axes and only then distributes leftover space to fr tracks. A layout that behaves in one is not guaranteed to behave in the other.

References

  1. CSS Grid Layout Module Level 1 — W3C
  2. CSS Grid Layout Module Level 2 (subgrid) — W3C
  3. CSS Box Alignment Module Level 3 — W3C