Grid in CSS
CSS Grid Layout is a new promising phenomenon in the world of layout design. Using Grid, you can create layouts that were previously impossible to achieve in CSS. With it, the possibilities are almost limitless. Grid claims to be the "best system for layout design in HTML." In this article, I will try to figure out what this Grid is like, and share my knowledge with you.
Grid is supported by almost all browsers, so it's time to start learning!
Also read: Flexbox in CSS
Basic Knowledge
Grid is a grid with elements on it. You can place elements however you like. Imagine a chessboard and pieces; the Grid container is the board, and the elements are the pieces. And then place them as you wish.
Grid is a set of horizontal and vertical "lines" that intersect and create a grid of rows and columns. Elements can be placed in the grid based on the line number or the row/column number.
To understand the Grid layout, you need to know what this grid specifically consists of. For this, study the diagram below and its description carefully.

Description of the Grid
-
Container — contains the Grid layout, where the elements reside.
-
Elements — HTML elements within the grid. These will be the first-level HTML elements (direct children of the container). For an element to appear in the grid, it must contain something (text, other HTML tags) inside it. An empty element is merely a cell for placing something in it.
-
Lines — these are imaginary lines (in reality, there are no lines) that divide the grid into columns and rows, creating the grid structure. Lines are automatically numbered. You can also assign names to lines so that you can later attach elements to them by number or by line name. Essentially, a line is a number or name of a column/row. The distance between lines (columns/rows) can be specified using grid-gap:, grid-row-gap:, grid-column-gap:.
-
Row/Column (track) — everything that is between adjacent lines, i.e., lines divide the grid into rows and columns.
-
Cell — the place where an element will be located. A cell is the intersection of a column and a row.
-
Area (field) — a combination of one or more cells into a single cell (field). This is a larger cell, also bounded by lines. Areas can be given a name for convenient placement of elements.
- Gap — the distance between rows and columns. It splits the line into two. Thus, between lines, and consequently between columns/rows, empty space appears. This is a kind of margin, border-spacing between cells. By default, there is only one line between cells (cells are merged), but if a gap is specified, we will split the line, and space will appear between columns/rows, while the number or name of the line (column/row) remains the same.
To enable Grid, any HTML element just needs to have the css property display:grid;
or display:inline-grid;
.
.grid { display: grid; } .inline-grid { display: inline-grid; }
<div class="grid"> <div>display: grid</div> </div> Text that contains <div class="inline-grid"> <div>display: inline-grid</div> </div> continued text

After enabling grid properties, a grid layout is created inside the container, and all nested elements (first level) become grid cells.
.grid { display: grid; }
<div class="grid"> <div>1</div> <div>2</div> </div>

Example of creating a grid block with two columns and three rows of different sizes:
.grid{ display: grid; grid: 1fr 25% 30px / 40% 1fr; /* rows / columns */ grid-gap: 1em; height: 200px; }
<div class="grid"> <div class="item">item 1</div> <div class="item">item 2</div> <div class="item">item 3</div> <div class="item">item 4</div> <div class="item">item 5</div> <div class="item">item 6</div> </div>

Features of Grid
Grid elements can be placed across multiple grid fields at once. You can change the direction or placement of elements in the grid. Columns and rows can be named. You can create a grid template and place elements according to the template.
-
Column/Row Sizes. The grid can be created with fixed or flexible column/row sizes (width/height). Fixed sizes are px, em, %, while flexible is the new unit of measurement in grid fr (fraction - free space in the grid).
-
Element Placement. Elements can be placed in a specified location of the grid by indicating the column/row number or their name (if it exists). Or by binding the element to a Grid area (the area needs to be created). If a specific placement of the element in the grid is not specified, the element will be placed by default in the first available cell: like in flex: horizontally (→) or vertically (↓). The default behavior can be changed using the grid-auto-flow: property.
-
Element Alignment. Elements within a cell can be aligned horizontally/vertically. The alignment is applied to the nested element within the cell, not the cell itself. For example, if there is a nested first-level element (this is the cell) inside the container, and it contains "text" or some "div" (text or div - this is the actual element), the alignment will align the nested element within the cell (the sizes of the cell will not change).
-
Multiple Elements in One Cell. Multiple elements can be placed in one cell or area. To specify who is "above" (more important) and who is "below" (less important), you need to use the css property z-index:.
- Grid Expansion. The number of columns/rows in the grid is usually specified right away, but if you place an element outside the grid (specifying a row/cell number that goes beyond the grid), then the grid will automatically expand, and additional lines (columns/rows) will be created.
CSS Grid Properties
For the Container
- display:
Enables the grid property for the element. This property includes the element itself and its nested elements: only first-level descendants are affected - they will become grid items of the grid container.
- grid - the element stretches to full width and has its own complete space among surrounding blocks. Line breaks occur at the beginning and end of the block.
- inline-grid — the element is wrapped by other elements. Its inner part is formatted as a block element, while the element itself is formatted as inline.
grid and inline-grid differ in how they interact with surrounding elements, similar to display:block and display:inline-block.
- grid-template-rows:
- grid-template-columns:
Specify how many rows (lines) and how many columns the grid consists of and what their sizes are. That is, both concepts are specified at once: how many and what size.
The values are specified with spaces: the height of the row (rows) or the width of the column (columns). The number of times the size is specified determines the number of rows/columns.
// syntax: grid-template-rows: size size ...; grid-template-columns: size size ...; grid-template-rows: [line-name] size [line-name] size ... [last-name]; grid-template-columns: [line-name] size [line-name] size ... [last-name];
-
size — this is the height of the row or the width of the column, which can be:
-
auto — the size of the row/column adjusts to the sizes of the elements, so that the largest one fits. It does not allow shrinking below the min-width or min-height of the widest or tallest element, respectively. It does not allow stretching beyond max-content. If there is free space in the container, the size can stretch to the end of the container.
-
px, em, %, vh, vw — size absolute (px, pt), relative (em, vw, vh) or in
%
of the container's width/height. -
fr (fraction - free space in the grid) — a special unit of measurement in grid. The free space in the container is divided into fractions, so if one column is specified as 1fr, and another as 2fr, the second will be twice as large as the first, and both will fill all the free space. Similar to flex-grow: in flex. Fractional values can also be specified here: 0.5fr, 2.3fr.
-
min-content — the smallest size of the content. For text, this is the width of the longest word or unbreakable fragment.
-
max-content — the largest size of the content. For text, this is the length of the longest line without breaks.
-
fit-content(max) — a function that receives a maximum size. If the content is smaller than this size, it behaves like auto; if larger, it limits the size of the row/column to the specified max parameter.
- minmax(min, max) — a function that allows you to specify both the minimum and maximum size at once.
-
-
line-name (line name) — before the size, you can specify (create) a name for the line (row/column). The name is specified in square brackets
[name] 100px
. You can also specify multiple names separated by spaces inside square brackets:[name another_name] 100px
. Any characters can be used in the name, including Cyrillic. - last-name (last name) — the specified name will become the name of the starting line of the row/column, but the row/column consists of two lines (they have size). Thus, the line name is the name of the start of one column (row) and the name of the end of the previous column (row). This way, the grid is filled, but in the end, there remains a row/column, and the specified name for it is only the name of the starting line of this row/column, while the end line has no name. Such a name for the last line can be specified at the end. That is, it works like this:
[name] 100px [name2] 100px [last-name]
.
These two properties have shorthand notations:
Examples of values:
// grid-template-columns: can take all the same values grid-template-rows: none; grid-template-rows: auto auto; grid-template-rows: 100px 1fr; grid-template-rows: [linename] 100px; grid-template-rows: [linename1] 100px [linename2 linename3]; grid-template-rows: minmax(100px, 1fr); grid-template-rows: fit-content(40%); grid-template-rows: repeat(3, 200px); grid-template-rows: 200px repeat(auto-fill, 100px) 300px; grid-template-rows: minmax(100px, max-content) repeat(auto-fill, 200px) 20%; grid-template-rows: [linename1] 100px [linename2] repeat(auto-fit, [linename3 linename4] 300px) 100px; grid-template-rows: [linename1 linename2] 100px repeat(auto-fit, [linename1] 300px) [linename3]; // global grid-template-rows: inherit; grid-template-rows: initial; grid-template-rows: unset;
Examples:
Let's create a grid (container) with three columns and three rows, the last column and row will have the same name 'main'.
.grid { grid-template-columns: 100px 10% [main] 800px; grid-template-rows: 100px 10% [main] 800px; }
If no name is specified, the row/column automatically receives two ordinal numerical names: positive and negative:
grid-template-columns: 40px 50px auto 50px 40px; grid-template-rows: 25% 100px auto;
Let's specify specific names (note how the name for the last line is specified):
grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end]; grid-template-rows: [row1-start] 25% [row1-end] 100px [third-line] auto [last-line];
The menu can have not just one, but several names, this example adds two names row1-end and row2-start:
grid-template-rows: [row1-start] 25% [row1-end row2-start] 25% [row2-end];
If there are repeating parts in the column sizes, you can use the repeat() function:
grid-template-columns: repeat(3, 20px [col-start]) 5%; // as a result we get: grid-template-columns: 20px [col-start] 20px [col-start] 20px [col-start] 5%;
If several rows have the same name, they can be referenced by name and ordinal number:
.item { grid-column-start: col-start 2; }
fr
allows you to specify a size relative to the remaining free space in the container. In this example, the free space in the container is divided by the number of specified fractions (we have 3) and the width is determined for each. So if the container width is 90px, each column will be 30px wide.grid-template-columns: 1fr 1fr 1fr;
The free space is calculated after the fixed sizes (including gaps) are accounted for. In this example, the size of the free space is calculated as the container's width minus 50px.
grid-template-columns: 1fr 50px 1fr 1fr;
-
- grid-template-areas:
Allows you to create a visual grid template. In this property, names are assigned to cells, and then elements are bound to these names through the grid-area: property specified for each element.
The syntax is great because it visually shows what the grid looks like:
grid-template-areas: "name name2 name3" "name name4 name5" "name none ."; // or it can be written like this grid-template-areas: "name name2 name3" "name name4 name5" "name6 none ."; // or single quotes grid-template-areas: 'name name2 name3' 'name name4 name5' 'name6 none .';
-
"name name2 name3"
— in the value inside quotes, names must be specified with spaces. Each set of quotes with names will represent a row of the grid, and the names inside the quotes assign names to the cells within that row. -
"name name name2"
— if the same name is specified multiple times in a row, the name will merge the cells, and we will get an area (a larger cell). Cells can be merged in this way not only within a row but also between rows. -
.
(dot) — is specified instead of a name and denotes a cell that should be skipped (an empty cell). Multiple dots can be used in a row; as long as there is no space between them, they will be counted as one. none
— the area is undefined.
Notes:
- Any characters can be used in the name, including Cyrillic.
-
Each row must have the same number of cells.
-
When using this method, lines (including the last line) are automatically given names. For example, if the area is called
bar
, then the name of the starting line of the row and column for this area will bebar-start
, and the last name will bebar-end
. This means that some lines will have multiple names. For example, the far left line from the example below (page template) will have three names:header-start
,main-start
andfooter-start
.The same logic works in reverse; if lines are named
home-start
andhome-end
, then everything between these lines becomes an area (cell) namedhome
.
grid-template-areas: can also be specified in the first value of the properties:
Examples:
Page template. Let's create a grid with four columns and three rows. The entire top row will be the header (header), the middle row will be content (main) and sidebar (sidebar), leaving space (.) between them. The last row will be the footer (footer).
.item-a { grid-area: header; } .item-b { grid-area: main; } .item-c { grid-area: sidebar; } .item-d { grid-area: footer; } .container { grid-template-columns: 50px 50px 50px 50px; grid-template-rows: auto; grid-template-areas: "header header header header" "main main . sidebar" "footer footer footer footer"; }
You can specify sizes and areas simultaneously:
.grid { display: grid; grid-template-rows: [header-top] minmax( 6em, auto) [header-bottom main-top] 1fr [main-bottom]; grid-template-columns: 1fr 30%; grid-template-areas: "header header" "main menu"; } .header { grid-area: header; } .main { grid-area: main; } .menu { /* attachment to the area (cell) */ grid-area: menu; /* the same attachment, but to the names of the lines of the row */ grid-row: main-top / main-bottom; /* or */ grid-row: menu-start / menu-end; /* or */ grid-row: 2 / 3; }
-
- grid-template:
Allows you to specify three properties at once: grid-template-rows, grid-template-columns and grid-template-areas.
// syntax: grid-template: none; grid-template: grid-template-rows / grid-template-columns; grid-template: [ line-names? "row" size? line-names? ] ... grid-template: [ line-names? "row" size? line-names? ] ... / [ line-names? size ] ... line-names? // what is in [] or before ? — can be omitted // ... — can be repeated
This property can be replaced with the shorthand grid: grid-template-rows / grid-template-columns
Examples:
In the first parameter (in rows), you can specify a template (area). 25px is the height of the row.
grid-template: "header header header" 25px "footer footer footer" 25px / auto 50px auto;
You can also specify names for the line of the row:
grid-template: [header-top] "a a a" [header-bottom] [main-top] "b b b" 1fr [main-bottom] / auto 1fr auto; // as a result, this will be obtained: grid-template-areas: "a a a" "b b b"; grid-template-rows: [header-top] auto [header-bottom main-top] 1fr [main-bottom]; grid-template-columns: auto 1fr auto;
grid-template: does not reset the properties for rows/columns to default: grid-auto-columns:, grid-auto-rows:, grid-auto-flow:. To do that, it's better to use the grid: property instead of grid-template:.
- repeat() — function
Allows you to repeat something N times. Used when creating columns/rows in properties: grid-template-rows:, grid-template-columns:, grid-template:.
repeat( how_many_times, what_to_repeat )
Options for using repeat():
-
Repeating any number of rows/columns a specified number of times.
repeat( 2, 50px ) // 50px 50px repeat( 2, 50px 70px ) // 50px 70px 50px 70px
-
Repeating one row/column until the container is filled. The number of repetitions is specified through:
- auto-fill — repeats the row/column as long as there is space in the container. At least one repetition will always be there.
- if the container has a maximum size, the row/column repeats as long as there is space to insert the next row/column.
- if the container has a minimum size, the row/column repeats until that minimum is not exceeded.
- auto-fit — the same as above, but after placing elements, the remaining empty row/column shrinks and disappears, resulting in the container always looking filled (without empty spaces at the edges) (this is visually noticeable only if there is a flexible block in the container).
- auto-fill — repeats the row/column as long as there is space in the container. At least one repetition will always be there.
Limitations of repeat():
- You cannot nest repeat() within repeat().
-
With auto-fill and auto-fit, you can only repeat non-flexible sizes (having a finite width/height). Thus, it makes no sense to use min-content, max-content, fr, auto - all of them are flexible, and as a result, we will only get one row/column without any repetitions.
repeat( auto-fill, 100px ) // allowed repeat( auto-fit, minmax(100px, 1fr) ) // allowed repeat( auto-fill, auto ) // not allowed repeat( auto-fit, 1fr ) // not allowed
-
repeat() with
auto-fill
orauto-fit
can only be one for one property (grid-template-rows: or grid-template-columns:).// allowed grid-template-columns: repeat(3, 50px 5%) repeat(auto-fill, 100px) repeat(4, 80px) // not allowed (2 repeats with an unknown number of repetitions) grid-template-columns: repeat(auto-fill, 50px) repeat(2, 100px) repeat(auto-fit, 80px)
- If there is a repeat() with
auto-fill
orauto-fit
, then in other repeats for that same property, you cannot repeat imprecise sizes: *-content, fr or auto.// allowed (in the first repeat a specific width in percentage is repeated) grid-template-columns: repeat(4, 10%) repeat(auto-fill, 10em) // not allowed (in one repeat there is auto-fill, and in the other repeat a previously unknown width is repeated). grid-template-columns: repeat(auto-fill, 100px) repeat(4, 1fr)
Examples:
-
- minmax() — function
Allows you to specify minimum and maximum values for a row/column. Used in properties where you need to specify size: grid-template-rows:, grid-template-columns:, grid-template:.
minmax( min_value, max_value )
Value can be:
- px, em, rem, ... — length units (100px)
- % — percentages (10%)
-
fr — flexible sizes. Can only be used for the max value.
-
max-content — the smallest possible size of a cell, at which the content fits freely in it. For example, if there is text in the cell, the ideal width of the cell will be the entire length of the text (in one line without breaks).
-
min-content — the smallest size at which the cell does not overflow. For example, if there is text in the cell, the width will be equal to the longest word in the text.
-
auto — depends on whether it is used as maximum or minimum value in the minmax() function:
- if as maximum, then it is the same as max-content.
- if as minimum, then the value will be the minimum possible size for the cell. This size differs from min-content and is set by the properties min-width or min-height.
Examples:
Usage options:
grid-template-columns: minmax( 100px, 200px ) 1fr 1fr; grid-template-columns: minmax( 200px, 50% ) 1fr 1fr; grid-template-columns: minmax( 200px, 1fr ) 1fr 1fr; grid-template-columns: minmax( min-content, max-content ) 1fr 1fr; grid-template-columns: minmax( auto, auto ) 1fr 1fr; grid-template-columns: repeat( auto-fit, minmax(200px, 1fr) );
If the specified maximum value turns out to be less than the minimum, it is ignored, and only the min value works.
For a detailed description of the examples, see the article: how minmax() works.
- grid-row-gap:
- grid-column-gap:
- grid-gap:
grid-row-gap: sets the gap between rows.
grid-column-gap: sets the gap between columns.
grid-gap: sets the gap for rows and columns at once. This is shorthand for the two previous properties. If one parameter is specified, it will be set for both values.// syntax: grid-row-gap: size; grid-column-gap: size; grid-gap: size size; /* row column */ grid-gap: size;
Size can be absolute (px, pt), relative (%, em).
Examples:
grid-column-gap: 10px; grid-row-gap: 15px;
Note: the prefix
grid-
will be removed, and the property names will become: column-gap: and row-gap:. Chrome 68+, Safari 11.2 and Opera 54+ already support properties without this prefix.- align-content:
- justify-content:
- place-content:
Aligns rows/columns. The grid cells are aligned, not the elements inside the cells. To align elements, use: justify-items, align-items, place-items.
// syntax: align-content: value; // aligns rows vertically ↓↑ justify-content: value; // aligns columns horizontally ←→ place-content: value; // shorthand: will set both values place-content: align-content justify-content; // shorthand
Value can be:
- stretch (default) — stretches the rows/columns (cells) completely. All space of the container is filled. Makes sense only if rows/columns do not have a fixed size (they are flexible). If they are not flexible, it works like start.
- start — rows/columns are packed tightly together to the starting edge of the container.
- end — rows/columns are packed tightly together to the ending edge of the container.
-
center — rows/columns are packed tightly together and are in the middle of the container.
- space-around — free space is evenly distributed between rows/columns and added at the edges. This results in the outer row/column not being pressed against the edges of the container, but the distance to the edge is half that of the distance between rows/columns.
- space-evenly — the same as space-around, except the distance to the edges of the container is the same as that between rows/columns.
- space-between — the outer row/column is pressed against the edges of the container, and the free space is evenly distributed between rows/columns.
Rare values:
-
safe value — if the width of the column or height of the row exceeds the size of the container, the value switches to start. In this value, only center or end can be used.
-
unsafe value — the value remains specified even if the width of the column or height of the row exceeds the size of the container. In this value, only center or end can be used.
safe unsafe alignment
Examples:
Full syntax:
The above was not the complete syntax for simplicity, but it covers 99% of the needed.
// syntax: align-content: normal | <distribution> | <overflow>? <position> | <baseline> justify-content: normal | <distribution> | <overflow>? [ <position> | left | right ] // <distribution> = space-between | space-around | space-evenly | stretch // <overflow> = unsafe | safe // <position> = center | start | end | flex-start | flex-end // <baseline> = [ first | last ]? baseline // what is in [] or before ? — can be omitted
- left — for justify-content, does not work for align-content. Columns are packed tightly together to the left edge of the container.
-
right — for justify-content, does not work for align-content. Columns are packed tightly together to the right edge of the container.
-
flex-start — the same as start for the grid. That is, if the elements are not children of a flex container, it behaves like start.
-
flex-end — the same as end for the grid. That is, if the elements are not children of a flex container, it behaves like end.
- baseline, first baseline, last baseline — aligns to the text line (first or last). Backward compatibility: for first baseline, this is start, and for last baseline, this is end.
All value options:
align-content:
// basic alignment align-content: center; align-content: start; align-content: end; align-content: flex-start; align-content: flex-end; align-content: normal; // text line alignment align-content: baseline; align-content: first baseline; align-content: last baseline; // distribution align-content: space-between; align-content: space-around; align-content: space-evenly; align-content: stretch; // when an element overlaps the container align-content: safe center; align-content: unsafe center; // globally align-content: inherit; align-content: initial; align-content: unset;
justify-content:
// basic alignment justify-content: center; justify-content: start; justify-content: end; justify-content: flex-start; justify-content: flex-end; justify-content: left; justify-content: right; align-content: normal; // text line alignment makes no sense // distribution justify-content: space-between; justify-content: space-around; justify-content: space-evenly; justify-content: stretch; // when an element overlaps the container justify-content: safe center; justify-content: unsafe center; // globally justify-content: inherit; justify-content: initial; justify-content: unset;
place-content:
/* basic alignment */ /* align-content cannot be left or right */ place-content: center start; place-content: start center; place-content: end left; place-content: flex-start center; place-content: flex-end center; /* text line alignment */ /* for the second parameter (justify-content) makes no sense */ place-content: baseline center; place-content: first baseline space-evenly; place-content: last baseline right; /* distribution */ place-content: space-between space-evenly; place-content: space-around space-evenly; place-content: space-evenly stretch; place-content: stretch space-evenly; /* globally */ place-content: inherit; place-content: initial; place-content: unset;
- align-items:
- justify-items:
- place-items:
Aligns grid elements - that is, what is inside the grid cells. It applies to all grid elements. To align the grid cells (rows and columns) themselves, use: justify-content, align-content, place-content.
// syntax: align-items: value; // aligns elements vertically ↓↑ justify-items: value; // aligns elements horizontally ←→ place-items: value; // shorthand: will set both values place-items: align-items justify-items; // shorthand
Value can be:
-
auto (default) — indicates to use the justify-items: value set for the container. If the element is positioned using absolute, then normal is applied.
- stretch — stretches all elements to the full width/height of the cells.
- start — places all elements at the start of the cells (left or top).
- end — places all elements at the end of the cells (right or bottom).
- center — places all elements in the center of the cells.
Rare values:
-
safe value — if the width or height of the element exceeds the size of the cell, the value switches to start. In this value, only center or end can be used.
-
unsafe value — the value remains specified even if the width or height of the element exceeds the size of the cell. In this value, only center or end can be used.
safe unsafe alignment
Examples:
Full syntax:
The above was not the complete syntax for simplicity, but it covers 99% of the needed.
// syntax: align-items: normal | stretch | [ <overflow>? <position> ] | <baseline> justify-items: normal | stretch | <overflow>? [ <position> | left | right ] | <baseline> | legacy [ left | right | center ] // <baseline> = [ first | last ]? baseline // <overflow> = unsafe | safe // <position> = center | start | end | self-start | self-end | flex-start | flex-end // what is in [] or before ? — can be omitted
- left — for justify-items, does not work for align-items. All elements are pressed against the left edge of the cells.
-
right — for justify-items, does not work for align-items. All elements are pressed against the right edge of the cells.
-
flex-start — the same as start for the grid. That is, if the elements are not children of a flex container, it behaves like start.
-
flex-end — the same as end for the grid. That is, if the elements are not children of a flex container, it behaves like end.
-
baseline, first baseline, last baseline — aligns to the text line (first or last). Backward compatibility: for first baseline, this is start, and for last baseline, this is end.
- legacy — allows you to control how the value will be inherited by children.
If legacy is specified without left, right, center, then the value is simply inherited from the parent or equals normal.
When justify-self:auto refers to the value of justify-items:, the value without the word legacy is taken, for example, legacy left will take left. This is needed for the correct alignment of the <center> tag or an element with the align attribute.
All value options:
align-items:
// basic values align-items: normal; align-items: stretch; // position alignment // align-items cannot be left or right align-items: center; align-items: start; align-items: end; align-items: flex-start; align-items: flex-end; align-items: self-start; align-items: self-end; // text line alignment align-items: baseline; align-items: first baseline; align-items: last baseline; // when an element overlaps the container align-items: safe center; align-items: unsafe center; // globally align-items: inherit; align-items: initial; align-items: unset;
justify-items:
// basic values justify-items: auto; justify-items: normal; justify-items: stretch; // position alignment justify-items: center; justify-items: start; justify-items: end; justify-items: flex-start; justify-items: flex-end; justify-items: self-start; justify-items: self-end; justify-items: left; justify-items: right; // text line alignment makes no sense // when an element overlaps the container justify-items: safe center; justify-items: unsafe center; // globally justify-items: inherit; justify-items: initial; justify-items: unset;
place-items:
// basic values place-items: auto center; place-items: normal start; // position alignment place-items: center normal; place-items: start auto; place-items: end normal; place-items: self-start auto; place-items: self-end normal; place-items: flex-start auto; place-items: flex-end normal; place-items: left auto; place-items: right normal; // text line alignment place-items: baseline normal; place-items: first baseline auto; place-items: last baseline normal; place-items: stretch auto; // globally place-items: inherit; place-items: initial; place-items: unset;
-
- grid-auto-rows:
- grid-auto-columns:
Sets the size for auto-created (implicit) rows/columns.
Columns/rows are created automatically when there are more elements in the grid than fit into the specified rows/columns (cells) or when an element is placed outside the specified grid.
// syntax: grid-auto-columns: size; grid-auto-rows: size;
Possible size values can be found in the description of grid-template-rows: and grid-template-columns: — it can be px, em, %, fr, etc.
Examples:
To understand how auto-cells in the grid are created, let's imagine we have a 2x2 grid - 2 rows and 2 columns:
grid-template-columns: 60px 60px; grid-template-rows: 90px 90px
Now, let's try to place elements in the grid by specifying their positions through properties:
.item-a { grid-column: 1; grid-row: 2; } .item-b { grid-column: 5; grid-row: 2; }
The element .item-b we asked to be placed in the non-existent column 5. We only have 2 columns, so the grid will automatically expand, and 3 more columns will be added with auto width. The current properties allow us to set sizes for such implicit columns:
grid-auto-columns: 60px;
A interesting point for implicit rows and repeat():
- grid-auto-flow:
Defines the logic for adding elements to empty cells.
When elements are not specifically indicated in which cell (area) they should be placed, they are placed in empty cells. By default, filling goes left to right (→) until the end of the row, then moving to the next row and filling again (similar to how we read text).
By default, the algorithm is
row
i.e. → ↓ →, it can be made ↓ → ↓.// syntax: grid-auto-flow: row | column | row dense | column dense
-
row (default) — places elements in the grid by rows → ↓ →. Adds rows if there is no space in the grid.
-
column — places elements in the grid by columns ↓ → ↓. Adds columns if there is no space in the grid.
- dense — enables the "dense" packing algorithm, which tries to place elements so that no empty spaces remain (by default, the "sparse" algorithm works). In this case, elements may be placed out of order. When inserting each subsequent element, the algorithm checks if there is an empty cell behind where the current element can fit; if there is, it places it there instead of the next cell. Dense makes sense only when there are areas in the grid - this is when the element is instructed to span multiple cells (through span), then it generally does not fit into one cell and leaves empty space.
Examples:
Suppose we have the following HTML:
<div class="grid"> <div class="item-a">item-a</div> <div class="item-b">item-b</div> <div class="item-c">item-c</div> <div class="item-d">item-d</div> <div class="item-e">item-e</div> </div>
Now, we created a grid with 5 columns and two rows, and two elements are placed in specific cells:
.grid { display: grid; grid-template: 60px 60px 60px 60px 60px / 30px 30px; /* by default: grid-auto-flow: row; */ }
Now let's set
grid-auto-flow: column
:-
- grid:
Allows you to write shorthand for the properties:
- grid-template-rows:
- grid-template-columns:
- grid-template-areas:
- grid-auto-rows:
- grid-auto-columns:
- grid-auto-flow:
That is, in this property, you can describe almost all properties of the grid. However, properties can only be described for "explicit" or "implicit" rows/columns; it is not possible to do both at once. If you need to specify properties for both, you need to add the corresponding properties in addition to grid:.
// syntax: grid: none grid: grid-template grid: grid-template-rows / grid-template-columns grid: grid-template-areas grid: grid-template-rows / [auto-flow dense?] grid-auto-columns? grid: [auto-flow dense?] grid-auto-rows? / grid-template-columns // what is in [] or before ? — can be omitted
-
none — resets all properties to their initial state.
-
grid-template-rows / [auto-flow +dense?] grid-auto-columns? — the keyword
auto-flow
on the right side of the slash sets grid-auto-flow: column.dense
enables the "dense" packing algorithm. If grid-auto-columns is not specified, it will default to auto. - [auto-flow +dense?] grid-auto-rows? / grid-template-columns — the keyword
auto-flow
on the left side of the slash sets grid-auto-flow: row.dense
enables the "dense" packing algorithm. If grid-auto-rows is not specified, it will default to auto.
Examples:
grid: 'header header header header' 'main main main right right' 'footer footer footer footer'; // same as: grid-template-areas: 'header header header header' 'main main main right right' 'footer footer footer footer';
grid: 100px 300px / 3fr 1fr; // same as: grid-auto-flow: row; grid-template-columns: 200px 1fr;
grid: auto-flow dense 100px / 1fr 2fr; // same as: grid-auto-flow: row dense; grid-auto-rows: 100px; grid-template-columns: 1fr 2fr;
grid: 100px 300px / auto-flow 200px; // same as: grid-template-rows: 100px 300px; grid-auto-flow: column; grid-auto-columns: 200px;
You can also specify a more complex but convenient setup for everything at once:
grid: [row1-start] "header header header" 1fr [row1-end] [row2-start] "footer footer footer" 25px [row2-end] / auto 50px auto; // same as: grid-template-areas: "header header header" "footer footer footer"; grid-template-rows: [row1-start] 1fr [row1-end row2-start] 25px [row2-end]; grid-template-columns: auto 50px auto;
And more options:
grid: repeat(auto-fill, 5em) / auto-flow 1fr; grid: auto-flow 1fr / repeat(auto-fill, 5em); grid: auto 1fr auto / repeat(5, 1fr); grid: repeat(3, auto) / repeat(4, auto);
For Elements
CSS properties: float, display:inline-block, display:table-cell, vertical-align, and column-* do not affect grid container elements. Grid has its own rules...
- grid-row-start:
- grid-row-end:
- grid-column-start:
- grid-column-end:
- grid-row:
- grid-column:
Indicates the position of the element in the grid. That is, it places the element in the specified cell. You need to specify the name or number of the line to which the cell relates and to which the element should be attached.
grid-column
andgrid-row
are shorthand for the properties:grid-column-start/grid-column-end
andgrid-row-start/grid-row-end
. You can specify only the first (one) value, it will refer to the starting line, and the element will stretch over 1 row/column (i.e., it will be placed in one cell whose starting line is specified).// syntax: grid-row-start: value; // where the row line starts grid-row-end: value; // where the row line ends grid-column-start: value; // where the column line starts grid-column-end: value; // where the column line ends grid-row: grid-row-start / grid-row-end; grid-column: grid-column-start / grid-column-end; // you can specify one value, the second value will be span 1 grid-row: grid-row-start; grid-column: grid-column-start;
The value can be:
-
number/name — the ordinal number or name of the line to which the current element should be attached.
-
span number — the word span means to stretch. The current element will stretch over the specified number of rows/columns. If the word span is specified, it always refers to the second value.
-
span name — the word span means to stretch. The current element will stretch to the specified name of the row/column line. If the word span is specified, it always refers to the second value.
- auto — the element is placed according to the specified algorithm in the container property grid-auto-flow:.
Examples:
.item-a { grid-column-start: 2; grid-column-end: five; grid-row-start: row1-start grid-row-end: 3 }
.item-b { grid-column-start: 1; grid-column-end: span col4-start; grid-row-start: 2 grid-row-end: span 2 }
If the properties grid-column-end/grid-row-end are not set, the element will, by default, stretch over 1 row/column.
Elements can overlap each other, in such cases priority can be set via z-index:.
Example of shorthand line specification.
grid-column: 3 / span 2; grid-row: third-line / 4;
-
- grid-area:
Gives the element a name. By name, the element will refer to the area specified in the property grid-template-areas:. Alternatively, you can specify the number/name of the lines.
// syntax: grid-area: area name; grid-area: row-start / column-start / row-end / column-end;
- area name — the name of the grid area.
- row-start / column-start / row-end / column-end — can be a number or the name of the line.
Examples:
grid-area: header; // or like this: grid-area: 1 / col4-start / last-line / 6
- align-self:
- justify-self:
- place-self:
Aligns the current element within the cell. Applies to an individual element of the container.
// syntax: align-self: value; // aligns the element vertically within the cell ↓↑ justify-self: value; // aligns the element horizontally within the cell ←→ place-self: value; // shorthand: sets both values place-self: align-self justify-self; // shorthand
The value can be:
- stretch (default) — stretches the current element to the full width/height of the cell.
- start — places the current element at the beginning of the cell (left or top).
- end — places the current element at the end of the cell (right or bottom).
- center — places the current element in the center of the cell.
Rare values:
-
safe value — if the width or height of the element exceeds the size of the cell, the value switches to start. The value here can only be: center or end.
- unsafe value — the value remains specified, even if the width or height of the element exceeds the size of the cell. The value here can only be: center or end.
Examples:
To align all elements of the container at once, use: align-items:, justify-items:, place-items:. There you will also find a description of rarely used values.
Examples
# HTML Page Framework
body { display: grid; grid: "header header header" 80px "nav section aside" 1fr "footer footer footer" 50px / 15% 1fr 18%; min-height: 100vh; } header { grid-area: header; } nav { grid-area: nav; } section { grid-area: section; } aside { grid-area: aside; } footer { grid-area: footer; }
<body> <header>header</header> <nav>nav</nav> <section>section</section> <aside>aside</aside> <footer>footer</footer> </body>
# Framework for a Game Application

#grid { /** * Two columns: * 1. adjusts to content, * 2. takes all remaining space * (but cannot be less than the minimum size of the content in it or the controls block in the same column) * * Three rows: * 1. adjusts to content, * 2. takes all free space * (but cannot be less than the minimum size of any of the blocks in this row) * 3. adjusts to content. */ display: grid; grid-template-columns: /* 1 */ auto /* 2 */ 1fr; grid-template-rows: /* 1 */ auto /* 2 */ 1fr /* 3 */ auto; /* stretch to full height */ height: 100vh; } /* specifies the positions of blocks in the grid using coordinates */ #title { grid-column: 1; grid-row: 1; } #score { grid-column: 1; grid-row: 3; } #stats { grid-column: 1; grid-row: 2; } #board { grid-column: 2; grid-row: 1 / span 2; } #controls { grid-column: 2; grid-row: 3; justify-self: center; }
<div id="grid"> <div id="title">Game Title</div> <div id="score">Score</div> <div id="stats">Stats</div> <div id="board">Board</div> <div id="controls">Controls</div> </div>
Let’s complicate the task.
Now we need to make it so that when the mobile device is rotated, the framework changes to look like this:

In this case, it will be more convenient to use grid areas.
/* portrait orientation */ @media (orientation: portrait) { #grid { display: grid; /* create the grid structure and specify the area names, * This structure will work by default and will be suitable for landscape orientation. */ grid-template-areas: "title stats" "score stats" "board board" "ctrls ctrls"; /* specify sizes for rows and columns. */ grid-template-columns: auto 1fr; grid-template-rows: auto auto 1fr auto; height: 100vh; } } /* landscape orientation */ @media (orientation: landscape) { #grid { display: grid; /* create the grid structure and specify the area names, * This structure will work by default and will be suitable for landscape orientation. */ grid-template-areas: "title board" "stats board" "score ctrls"; /* specify sizes for rows and columns. */ grid-template-columns: auto 1fr; grid-template-rows: auto 1fr auto; height: 100vh; } } /* place elements in named grid areas */ #title { grid-area: title; } #score { grid-area: score; } #stats { grid-area: stats; } #board { grid-area: board; } #controls { grid-area: ctrls; justify-self: center; }
<div id="grid"> <div id="title">Game Title</div> <div id="score">Score</div> <div id="stats">Stats</div> <div id="board">Board</div> <div id="controls">Controls</div> </div>
This example needs to be viewed on a phone...
# Simple Block on Grid
# Masonry on Grid
Still no way without JS.
Comparison of Flex and Grid
Unlike Flex, which is oriented on one axis, Grid is optimized for two-dimensional layouts: when it is required to place (align) content in both dimensions (vertically and horizontally).
Moreover, due to the ability to explicitly position elements in the grid, Grid allows for radical transformations in structure without requiring any changes to the HTML markup. By combining media queries with CSS properties that control the layout of the grid container and its child elements, you can adapt the layout for any device form factors.
Grid and Flexbox have their own features, and it cannot be said that one replaces the other. Rather, Flex is a complement to Grid, or vice versa.
Flexbox focuses on distributing space within one axis, uses a simpler approach to layout, can utilize a content-based row packing system to manage its secondary axis, and relies on the hierarchy of markup. Whereas Grid is more suitable for creating frameworks because it has a more powerful and comprehensive approach and is generally independent of the markup hierarchy. In certain cases, Grid allows for creating responsive layouts that cannot be created with Flex or in any other way.
Browser Support
Grid is supported by {percent} of browsers currently in use.
To check if the current browser supports Grid in CSS, you can use @supports
(not working in IE):
@supports( display: grid ) { /* grid is supported by the browser */ div { display: grid; } } @supports not( display: grid ) { /* grid is NOT supported by the browser */ div { display: flex; } }
Videos on Grid
Links
Useful links on Grid:
-
Grids in the Garden — a game that allows you to practice with grids in an interesting way.
- grid.layoutit.com — an interactive grid layout generator.
Used when writing:
- Guide to Grids (css-tricks.com)