The College Fest Gallery Problem
Your college fest is over. The photography club has hundreds of photos: the main stage, the rangoli competition, the food stalls and the dance finals. The student council asks you to put the best ones on the fest website, neatly, in rows and columns, with one big photo of the opening night.
You could try this with Flexbox, which you met in the CSS Flexbox lesson. Flexbox is great for a single row of items, but making photos line up perfectly in both rows and columns, with one photo stretching across two rows, is hard work. This is exactly the job CSS Grid was made for.
In this lesson you will learn how to turn any box into a grid, how to size columns and rows, how to add space between them, and how to make one item cover several cells.
Your First Grid: display: grid
A grid has two parts. The grid container is the parent element. You give it display: grid. Its direct children automatically become grid items, and the browser places them into cells, one after another, left to right and then down to the next row.
On its own, display: grid gives you just one column. You decide the columns with grid-template-columns. Each value you write creates one column of that width:
<style>
.gallery {
display: grid;
grid-template-columns: 150px 150px 150px;
gap: 12px;
}
.gallery div {
background: #fde68a;
padding: 20px;
text-align: center;
border-radius: 8px;
}
</style>
<div class="gallery">
<div>Photo 1</div>
<div>Photo 2</div>
<div>Photo 3</div>
<div>Photo 4</div>
<div>Photo 5</div>
<div>Photo 6</div>
</div>
Three values, so three columns. The six photos fill two rows on their own. You did not create the second row: the browser made it because there were more items than cells. The gap property adds space between the cells, but not around the outside edge.
The fr Unit: Sharing Free Space
Fixed widths like 150px are risky. On a wide laptop they leave empty space, and on a small phone they overflow. Grid has its own unit for this: fr, short for fraction. One fr is one share of the free space in the container.
<style>
.page {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 10px;
}
.page div {
background: #bfdbfe;
padding: 20px;
text-align: center;
}
</style>
<div class="page">
<div>1fr</div>
<div>2fr (twice as wide)</div>
<div>1fr</div>
</div>
Here the space is cut into 4 shares (1 + 2 + 1). The middle column gets 2 shares, so it is always twice as wide as each side column, on any screen. The gap is taken away first, and then the rest is shared out.
You can also mix units. grid-template-columns: 200px 1fr gives a fixed 200 pixel sidebar and lets the main area take everything else.
repeat(), Rows and Gaps
Writing 1fr 1fr 1fr 1fr is boring and easy to get wrong. The repeat() function does it for you: repeat(4, 1fr) means “four columns of 1fr each”.
Rows work the same way:
grid-template-rowssets the height of the rows you list.grid-auto-rowssets the height of any extra rows the browser creates by itself.gapwith two values sets the row gap first, then the column gap. You can also writerow-gapandcolumn-gapseparately.
<style>
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: 100px 60px;
grid-auto-rows: 80px;
gap: 10px 20px;
}
.gallery div {
background: #c7d2fe;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
}
</style>
<div class="gallery">
<div>1</div><div>2</div><div>3</div><div>4</div>
<div>5</div><div>6</div><div>7</div><div>8</div>
<div>9</div><div>10</div><div>11</div><div>12</div>
</div>
Look at the result. The first row is 100px tall and the second is 60px, as we listed. Row three was not listed, so it uses grid-auto-rows: 80px. If you leave rows out completely, each row is just as tall as its tallest item, which is often what you want.
Open DevTools. Right-click the grid, choose Inspect, and click the small grid badge next to the element. Chrome, Edge and Firefox then draw the grid lines and line numbers on the page. It is the fastest way to understand any grid.
Placing Items: Grid Lines and span
Between and around every column there is a grid line. A grid with 4 columns has 5 vertical lines, numbered 1 to 5 from the left. Rows have numbered lines too. You can tell an item where to start and end using these numbers:
| Code | Meaning |
|---|---|
grid-column: 1 / 3 | Start at column line 1, end at line 3 (covers 2 columns) |
grid-column: 1 / -1 | From the first line to the last line (full width, for any number of columns) |
grid-column: span 2 | Cover 2 columns, starting wherever the item would normally go |
grid-row: span 2 | Cover 2 rows |
<style>
.board {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 70px;
gap: 8px;
}
.board div {
background: #e5e7eb;
padding: 8px;
border-radius: 6px;
}
.board .banner {
grid-column: 1 / 5; /* from line 1 to line 5 */
background: #fca5a5;
}
.board .wide {
grid-column: span 2; /* take 2 columns */
background: #86efac;
}
.board .tall {
grid-row: span 2; /* take 2 rows */
background: #93c5fd;
}
</style>
<div class="board">
<div class="banner">Banner: full width</div>
<div class="tall">Tall</div>
<div class="wide">Wide</div>
<div>Small</div>
<div>Small</div>
<div>Small</div>
<div>Small</div>
</div>
The banner covers lines 1 to 5, so it fills the whole first row. The tall box covers two rows, and the other items flow around it. Most of the time span is easier than line numbers, because you only say how big the item is and let the grid decide where it goes.
Grid or Flexbox?
Both are modern layout tools, and real websites use both. The main difference is the number of directions:
| Flexbox | Grid | |
|---|---|---|
| Directions | One: a row or a column | Two: rows and columns together |
| Who decides the size | Mostly the content of each item | Mostly the container’s columns and rows |
| Good for | Navigation bars, button groups, centring one thing | Galleries, dashboards, full page layouts |
Run this example and compare the last row in each box:
<style>
.flex-row, .grid-box {
gap: 8px;
margin-bottom: 24px;
}
.flex-row {
display: flex;
flex-wrap: wrap;
}
.flex-row div {
flex: 1 1 120px;
}
.grid-box {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
.flex-row div, .grid-box div {
background: #fbcfe8;
padding: 14px;
text-align: center;
}
</style>
<p>Flexbox (one direction, rows decide their own sizes):</p>
<div class="flex-row">
<div>A</div><div>B</div><div>C</div><div>D</div><div>E</div>
</div>
<p>Grid (rows and columns line up):</p>
<div class="grid-box">
<div>A</div><div>B</div><div>C</div><div>D</div><div>E</div>
</div>
With Flexbox, the last two items stretch to fill their row, so they do not line up with the items above. With Grid, D and E stay in the same columns as A and B. If you want things to line up in both directions, choose Grid.
Real-Life Example: The Utsav Fest Photo Gallery
Now let us build the gallery the student council asked for. It uses three equal columns, a fixed row height, a big featured photo that covers 2 columns and 2 rows, and a wide photo of the dance finals. The images come from an online placeholder service, so you need an internet connection to see them.
<style>
body {
font-family: system-ui, sans-serif;
background: #1e1b4b;
color: white;
padding: 16px;
}
h2 { text-align: center; }
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 140px;
gap: 10px;
}
.gallery figure {
margin: 0;
position: relative;
border-radius: 10px;
overflow: hidden;
}
.gallery img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.gallery figcaption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 6px 10px;
background: rgba(0, 0, 0, 0.6);
font-size: 14px;
}
.featured {
grid-column: span 2;
grid-row: span 2;
}
.wide { grid-column: span 2; }
</style>
<h2>Utsav 2026: Photo Gallery</h2>
<div class="gallery">
<figure class="featured">
<img src="https://picsum.photos/seed/fest1/600/400" alt="Main stage on the opening night">
<figcaption>Opening night, main stage</figcaption>
</figure>
<figure>
<img src="https://picsum.photos/seed/fest2/300/300" alt="Rangoli competition">
<figcaption>Rangoli</figcaption>
</figure>
<figure>
<img src="https://picsum.photos/seed/fest3/300/300" alt="Food stalls">
<figcaption>Food stalls</figcaption>
</figure>
<figure class="wide">
<img src="https://picsum.photos/seed/fest4/600/300" alt="Dance team on stage">
<figcaption>Group dance finals</figcaption>
</figure>
<figure>
<img src="https://picsum.photos/seed/fest5/300/300" alt="Robotics workshop">
<figcaption>Robotics workshop</figcaption>
</figure>
</div>
Notice object-fit: cover on the images. It makes each photo fill its cell completely without stretching, by cutting off the extra edges. Every photo has a short alt text for people who use screen readers.
Try these changes:
- Change
repeat(3, 1fr)torepeat(4, 1fr)and watch how the photos reflow. - Remove the class
featuredfrom the first photo. What happens to the layout? - Change
gap: 10pxtogap: 2pxfor a tight, magazine style look.
On a narrow phone, three columns may feel small. In the CSS Grid Layouts lesson you will learn a one-line trick that changes the number of columns by itself.
Common Mistakes
- Putting
display: gridon the items instead of the parent. Grid properties likegrid-template-columnsbelong on the container. Only placement properties likegrid-columngo on the items. See the broken and fixed versions below. - Using margins for spacing. Margins on grid items add space on the outside edges too, and the numbers get confusing. Use
gapon the container instead. - Fixed pixel columns everywhere.
repeat(4, 250px)needs 1000 pixels and will overflow a phone screen. Preferfr. - Counting columns instead of lines.
grid-column: 1 / 3covers 2 columns, not 3, because the numbers are lines. If this confuses you, usespan.
Here the grid is on the wrong element, so each card becomes its own tiny grid and the cards stack:
<style>
.card {
display: grid; /* on the cards, not on the parent */
grid-template-columns: 1fr 1fr 1fr;
}
.card { background: #fed7aa; padding: 12px; margin: 4px; }
</style>
<div class="cards">
<div class="card">Dance</div>
<div class="card">Music</div>
<div class="card">Drama</div>
</div>
Move the grid to the parent .cards and the cards sit side by side:
<style>
.cards {
display: grid; /* on the parent */
grid-template-columns: 1fr 1fr 1fr;
gap: 8px;
}
.card { background: #fed7aa; padding: 12px; }
</style>
<div class="cards">
<div class="card">Dance</div>
<div class="card">Music</div>
<div class="card">Drama</div>
</div>
Practice Task: The Fest Schedule Board
The fest committee wants a schedule board for Day 1. Use the starter code and complete the four TODO steps.
- The board has 3 equal columns with a 12px gap.
- The heading “Fest Schedule: Day 1” covers the full width.
- The Battle of Bands box is twice as tall as the others, because it is the main event.
<style>
body { font-family: system-ui, sans-serif; padding: 16px; }
.events div {
background: #ddd6fe;
padding: 16px;
border-radius: 8px;
}
/* TODO 1: make .events a grid with 3 equal columns */
/* TODO 2: add a gap of 12px */
/* TODO 3: make .headline span all 3 columns */
/* TODO 4: make .highlight take 2 rows */
</style>
<div class="events">
<div class="headline">Fest Schedule: Day 1</div>
<div class="highlight">Battle of Bands, 7 PM</div>
<div>Quiz, 10 AM</div>
<div>Coding contest, 11 AM</div>
<div>Treasure hunt, 2 PM</div>
<div>Debate, 4 PM</div>
</div>
Show one possible answer (try it yourself first!)
<style>
body { font-family: system-ui, sans-serif; padding: 16px; }
.events div {
background: #ddd6fe;
padding: 16px;
border-radius: 8px;
}
.events {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.headline {
grid-column: 1 / -1;
font-weight: bold;
text-align: center;
}
.highlight {
grid-row: span 2;
}
</style>
<div class="events">
<div class="headline">Fest Schedule: Day 1</div>
<div class="highlight">Battle of Bands, 7 PM</div>
<div>Quiz, 10 AM</div>
<div>Coding contest, 11 AM</div>
<div>Treasure hunt, 2 PM</div>
<div>Debate, 4 PM</div>
</div>
Key Takeaways
display: gridgoes on the parent. Its direct children become grid items.grid-template-columnsandgrid-template-rowsset the size of each track. Thefrunit shares out the free space.repeat(4, 1fr)is a short way to write four equal columns.gapadds space between cells, never on the outer edges.- Use
grid-columnandgrid-rowwith line numbers orspanto make an item cover several cells. - Flexbox is for one direction. Grid is for rows and columns together.