CSS Tutorial / CSS Grid: repeat, auto-fit, auto-fill, and Areas
CSS Grid can repeat tracks with repeat() and adapt the number of columns with auto-fit or auto-fill. The two automatic repetition modes differ in how they handle empty tracks; named areas provide another way to describe larger page regions.
Compare fixed and automatic repetition
.cards {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
repeat(3, ...) defines three columns regardless of the number of cards. A fourth automatically placed item goes into the next row; it does not increase the column count. This differs from automatic repetition, which calculates how many tracks fit. See CSS Grid Basics: Tracks, Lines, and Item Placement.
.cards {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 192px), 1fr));
}
auto-fit calculates how many columns will fit with the specified minimum size. The first value of minmax is the minimum, the second value is the maximum, and 1fr divides the remaining space. Here min(100%, 192px) ensures that the minimum column width is no larger than its parent. Fixing the minimum for columns to only 192px can cause even one column to overflow beyond its parent when the parent is narrower than 192px.
For an 800px container, 192px minimum columns, and 16px gaps, three columns need 608px, while four need 816px. Only three columns fit. This calculation determines the column count; subsequent free-space distribution determines their final widths. Long text still needs a wrapping strategy.
Compare auto-fill and auto-fit
| value | Empty repeat track after item placement | Large screen with few items |
|---|---|---|
| auto-fill | Keep empty tracks | Empty column space left |
| auto-fit | Collapse repeat tracks with no items | Occupied tracks expand into the available space |
Both values initially calculate the repeating columns that can go into them. The difference with auto-fit lies in the process of collapsing empty repeat tracks after placement. If items fill all columns, the two values may look similar, so make the containers wider and have fewer cards when comparing. Even when all cards fit into one column on a small screen, the difference is hard to see. Description of repeat() in MDN You can see the auto-repeat rules in.
Understand auto-placement and dense ordering
Items that do not specify a position are placed row-wise according to the default grid-auto-flow: row. Even if the previous item occupies a large area and an empty space is created, the default placement does not keep returning to the empty space while placing the next item. This default behavior makes HTML ordering and visual progression easier to understand.
grid-auto-flow: row dense can fill in the empty space in front with small items in the back. Although the space becomes tighter, the order in which it appears on the screen and the order of the DOM (Document Object Model) may differ. CSS placement does not reorder HTML, so keyboard focus and reading order must be considered together. For meaningfully numbered steps, chronological records, and form entries, maintaining order is more important than eliminating blank spaces. You can check the automatic placement conditions in MDN's grid-auto-flow description.
Run the automatic repetition example
Save the example as grid-auto-repeat.html. The first section has two cards in a container limited to 800px. The second section uses two links spanning two columns and two spanning one column, leaving a hole that lets you compare ordinary placement with dense.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-auto-repeat.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 192px), 1fr));
gap: 16px; width: min(100%, 800px); }
.cards > div { min-width: 0; padding: 16px; background: #dceefa; overflow-wrap: anywhere; }
.flow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px;
grid-auto-flow: row; max-width: 600px; margin-top: 16px; }
.flow > a { padding: 12px; background: #e8f5de; }
.flow > .wide { grid-column: span 2; }
</style>
</head>
<body>
<h1>Comparing empty repeated columns</h1><div class="cards"><div>First card</div><div>Second card</div></div>
<h2>Default auto-placement</h2><div class="flow"><a class="wide" href="#result">1: two spaces</a><a class="wide" href="#result">2: two spaces</a><a href="#result">3: One space</a><a href="#result">4: one space</a></div>
<p id="result">This is the destination of the automatic-layout link.</p>
</body>
</html>
- On a wide screen, if the first section is 800px, the two cards in auto-fit will take up 392px each. Replacing only auto-fit with auto-fill preserves the three columns, leaving the first two columns of cards at about 256px and an empty column. After confirmation, restore to auto-fit.
- Copy more divs to the first section to make it four. Three columns are filled and a fourth card is placed in the next row. In this condition, there are no empty repeating columns, which reduces the difference between the two values. After practice, return to the original two cards.
- Narrow the screen to make sure the parent width is less than 192px. Thanks to min(100%, 192px) one column will fit its parent. Changing the minimum to just 192px may cause overflow in tighter areas. After comparison, the original expression is restored.
- Check the empty space after link 1 and the location of link 3 in .flow. If you change only grid-auto-flow to row dense, number 3 will be placed in the previous empty space. With Tab, check whether the order 1 → 2 → 3 → 4 is maintained. Return to row after practice.
Define page regions with named areas
Rather than calculating column and row numbers separately, it is easier to read the page layout intent by expressing large sections with names such as header·main·aside·footer. Each string in grid-template-areas is one row, and names separated by spaces within the string are cells. The child's grid-area value must match its name to enter that area. How to change the column configuration based on screen width is explained in CSS Media Queries and Content-Based Breakpoints.
Repeating the same name across multiple cells creates one rectangular area. Areas cannot be shaped like an L or an isolated island. Each row must have the same number of cells, and cells to be left blank are indicated with a dot (.). If a declaration is invalid, the browser will not honor it, which may make it look like auto-placement. You can check the valid area conditions in MDN's grid-template-areas description.
Save the code below as grid-areas.html. On desktop, the body and supporting information are placed side by side, and at 640px or smaller, they are in one column. According to the screen layout, the HTML order was not reversed, but the main text came before the auxiliary information.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>grid-areas.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.page { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
grid-template-areas: "header header" "main aside" "footer footer"; gap: 16px; max-width: 1000px; }
.page > * { min-width: 0; padding: 20px; background: #dceefa; overflow-wrap: anywhere; }
header { grid-area: header; } main { grid-area: main; } aside { grid-area: aside; } footer { grid-area: footer; }
@media (max-width: 640px) {
.page { grid-template-columns: minmax(0, 1fr); grid-template-areas: "header" "main" "aside" "footer"; }
}
</style>
</head>
<body>
<h1>Page area names</h1><div class="page"><header>Site title</header><main><h2>Main content</h2><p>This is the main content area. The HTML order keeps it first on mobile, too.</p></main><aside>Related information</aside><footer>Contact and site information</footer></div>
</body>
</html>
- Make sure the header and footer occupy two columns on a wide screen. If you narrow the screen to 640px or less, header → main → aside → footer will be in one column. See why you need to change column definitions and area strings together.
- Change only the grid-area of aside to a different name. The connection between the aside area reserved in the template and the actual element may be disconnected and not placed in the intended slot. Restore the name to aside.
- If you change only the wide screen template to "header header" "header aside" "footer footer", the header will be L-shaped and the grid-template-areas declaration will be invalid. After confirming the application in Developer Tools, the original three strings are restored.
In practice, it's easier to maintain if you separate the area for creating columns based on the number of cards and the area for naming page sections. Auto-repeat determines the number and minimum width, while region names determine page structure and transition points. Before trying to force the two into one rule, decide what problem each container is trying to solve.










