AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Modern CSS: Grid and Flexbox Layouts

📚 Web Development⏱️ 20 min read🎓 Grade 11
✍️ AI Computer Institute Editorial Team Published: March 2026 CBSE-aligned · Peer-reviewed · 20 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

Modern CSS: Grid and Flexbox Layouts

Open any plain HTML page with no CSS layout rules and you will notice one thing: everything stacks straight down the page, one block under the next, like items dropped into a single vertical queue. A heading, then a paragraph, then an image, then another paragraph — top to bottom, left edge to left edge, no matter how wide your screen is. This is called the normal flow of HTML, and it is the browser's default behaviour. It works fine for a single article. It completely fails the moment you want a school website's navigation bar to show the logo on the left and three menu links lined up on the right, all sitting on one row, all vertically centered — or a marks report card where "Header" spans the full width, three subject boxes sit in a neat row below it, and a "Footer" spans the full width again underneath. Normal flow cannot do either of those things by itself.

Before 2014, web developers forced these layouts using tools that were never designed for the job: float (originally meant for wrapping text around an image, like a newspaper column) and display: inline-block (which left mysterious gaps caused by whitespace in your HTML source). Both required hacky fixes — an empty "clearfix" div here, a negative margin there — just to stop a layout from silently collapsing. CSS Flexbox and CSS Grid were built specifically to solve layout problems, and once major browsers supported them consistently, they replaced float-hacking as the standard way to arrange elements on a page. This chapter builds both from the ground up: what problem each one solves, the exact rules the browser follows to compute pixel widths (you will trace the arithmetic yourself, not just take it on faith), and when to reach for which tool.

A one-line refresher: the box model

Every HTML element the browser renders is a rectangular box with four layers, from the inside out: content (the text or image itself), padding (space inside the box, between content and border), border (a visible or invisible edge), and margin (space outside the box, separating it from its neighbours). Flexbox and Grid don't replace the box model — every flex item and every grid item is still a box with padding, border, and margin. What Flexbox and Grid change is how the browser decides where those boxes sit relative to each other and how wide or tall they become. Keep this in mind: when we say "the container is 800 pixels wide," we mean the space available for laying out child boxes inside it.

Flexbox: arranging items along one line

Flexbox turns on when you write display: flex; on a container element. The moment you do this, two things happen. First, every direct child of that container becomes a flex item and stops behaving like a normal block — it now lines up next to its siblings instead of stacking underneath them. Second, the browser defines two imaginary lines through the container: the main axis, which runs in the direction items are placed, and the cross axis, which runs perpendicular to it. By default, flex-direction is row, so the main axis runs left to right and the cross axis runs top to bottom.

Here is a school website navigation bar, the exact problem normal flow could not solve:

.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 24px;
  background: #0f172a;
}
.navbar .menu {
  display: flex;
  gap: 20px;
}

Trace what the browser does. .navbar has two children: a logo element and a .menu list. Because display: flex is set, both children become flex items placed along the main axis (a horizontal row, left to right). justify-content: space-between tells the browser: keep the first item pinned to the start of the main axis, the last item pinned to the end, and split any leftover horizontal space evenly into the gaps between items — that is exactly "logo on the left, menu on the right" in one rule, with no manual pixel math. align-items: center operates on the cross axis (vertical, since the main axis is horizontal here) and centers every item's vertical midpoint against the container's vertical midpoint — so a tall logo and a short menu still line up centered, regardless of their individual heights. Inside .menu, a second, independent display: flex with gap: 20px lays the individual links out in their own row with a fixed 20-pixel gap between each pair — no more counting margins or worrying about stray whitespace between inline elements.

Flexible sizing: flex-grow, flex-shrink, flex-basis

Flexbox items don't just sit where they're told — they can also stretch or shrink to fill available space, using the flex shorthand. Consider three subject-progress cards inside a 960-pixel-wide container, each styled flex: 1; with a 24-pixel gap between cards:

.card-row {
  display: flex;
  gap: 24px;
  width: 960px;
}
.card {
  flex: 1;
}

The shorthand flex: 1 expands to three values: flex-grow: 1; flex-shrink: 1; flex-basis: 0%;. A flex-basis of 0 means each card starts with zero natural width before growing. Now trace the arithmetic exactly the way the browser does. There are three cards and two gaps (between card 1–2 and card 2–3), so total gap space is 2 × 24 = 48 pixels. Subtracting that from the container leaves 960 − 48 = 912 pixels of free space to distribute. Since all three cards share the same flex-grow value of 1, that free space splits equally: 912 ÷ 3 = 304 pixels each. Because the basis was 0, each card's final width is exactly its share of the growth: 304 pixels. Check the total: 304 × 3 + 48 = 960 — it accounts for every pixel of the container. If one card instead had flex-grow: 2, it would claim twice the share of that 912-pixel pool as its neighbours — the grow factor is a ratio, not an absolute size.

Common misconception: "Flexbox lays things out in two dimensions, just like Grid"

This is wrong, and it is the single most common confusion students carry into Grid. Flexbox is strictly one-dimensional: it fully controls a single row (or a single column, if flex-direction: column) at a time. Add flex-wrap: wrap to a flex container and items that don't fit will drop to a second row — but that second row is laid out as its own independent flex line. If row 1 has three items of widths 200px, 350px, and 150px, and row 2 wraps with items of widths 300px and 250px, nothing forces the boundary between row 2's first and second item to line up underneath any particular boundary in row 1. Each wrapped row solves its own justify-content and sizing math from scratch, blind to what happened in the row above it. If your design genuinely needs columns to line up across multiple rows — like a marks table where every "Maths" column stays directly under the row above — Flexbox cannot guarantee that. That is precisely the gap Grid was built to fill.

Grid: rows and columns together

CSS Grid turns on with display: grid; and, unlike Flexbox, it lets you define rows and columns simultaneously, as one coordinated two-dimensional structure. Take a CBSE-style report card: a full-width header, three subject scores side by side beneath it, and a full-width footer. Grid's grid-template-areas property lets you literally draw this layout as ASCII art inside your CSS:

.report-card {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "maths  science english"
    "footer footer footer";
  gap: 16px;
}
.header  { grid-area: header; }
.maths   { grid-area: maths; }
.science { grid-area: science; }
.english { grid-area: english; }
.footer  { grid-area: footer; }

Each quoted string in grid-template-areas is one row, and each word inside it is one column's area name — since three columns were declared, every row string must list exactly three names. Repeating header three times in a row tells the browser "merge these three column cells into one wide cell named header," which is why the header and footer visually span the full width while maths, science, and english sit in their own separate columns beneath it. Every child then gets placed with a single line, grid-area: name;, matching the label drawn in the map above — no counting rows or columns by number required.

The fr unit: dividing leftover space

The unit fr (short for "fraction") only appears in Grid, and it means "one share of whatever space is left after fixed-size tracks and gaps are removed." Work through it with real numbers. Suppose grid-template-columns: 1fr 2fr 1fr; on a container that is exactly 800 pixels wide, with no gap. Add up the fr values: 1 + 2 + 1 = 4 total shares. Each share is worth 800 ÷ 4 = 200 pixels. Column 1 gets 1 × 200 = 200px, column 2 gets 2 × 200 = 400px, column 3 gets 1 × 200 = 200px. Check: 200 + 400 + 200 = 800 — accounted for exactly.

Now add a 20-pixel gap to that same three-column layout. Gaps are subtracted first, before fr values are computed. There are two gaps between three columns, so 2 × 20 = 40 pixels are removed up front, leaving 800 − 40 = 760 pixels to divide among the 4 total shares: 760 ÷ 4 = 190 pixels per share. The columns become 190px, 380px, and 190px. Check again: 190 + 380 + 190 + 40 (gaps) = 800.

Common misconception: "1fr means the column takes up 25% of the container"

This confuses fr with a percentage of the whole container — but fr only divides the space left over after every fixed-size track has already claimed its pixels. Prove it with a mixed layout: grid-template-columns: 150px 1fr 1fr; on an 800-pixel container, no gap. The first column is fixed at exactly 150px regardless of anything else. That leaves 800 − 150 = 650 pixels for the two 1fr columns to split evenly: 650 ÷ 2 = 325 pixels each. The final widths are 150px, 325px, 325px — note the two "equal" 1fr columns are each 325px, which is nowhere near a clean 25% or 33% of 800. If a sidebar widget on a website has a fixed 150px column next to it, growing or shrinking the fixed column directly changes how large every fr column becomes, because fr always reacts to whatever space remains, not to the container's total size.

Spanning multiple tracks

Grid items can also stretch across more than one column or row using grid-column or grid-row with the span keyword, without needing named areas. Picture a leaderboard of exam toppers arranged in a 3-column grid, where the rank-1 student's card should be visually bigger, spanning two columns instead of one:

.leaderboard {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.topper-rank-1 {
  grid-column: span 2;
}

repeat(3, 1fr) is just shorthand for writing 1fr 1fr 1fr three times. When the browser's auto-placement algorithm reaches .topper-rank-1, grid-column: span 2 tells it to occupy two of the three column tracks in whichever row it lands in, instead of the usual one — the very next item is then automatically pushed into the single remaining column of that row, or wraps to start a fresh row if no single column is left. This is something Flexbox's flex-grow cannot replicate precisely, because flex-grow only ever distributes leftover space proportionally — it has no concept of "occupy exactly 2 of 3 fixed grid tracks."

Flexbox — one dimension display: flex; flex: 1; 304px 304px 304px main axis (row) cross axis A wrapped 2nd row would NOT align to these columns CSS Grid — two dimensions display: grid; grid-template-areas HEADER MATHS SCIENCE ENGLISH FOOTER Every row's columns stay locked to the ones above

Choosing between Flexbox and Grid

The decision rule follows directly from the dimensionality difference just shown. If you are arranging items along a single line and letting their content decide how much space they naturally need — a navigation bar, a row of button icons, a horizontally scrolling list of cricket match cards — reach for Flexbox. If you are defining an overall page or component skeleton where rows and columns must stay locked together — a report card, an IRCTC-style train coach seat map with berth numbers arranged in fixed rows and columns, an exam timetable with days as columns and periods as rows — reach for Grid. Real production interfaces typically nest both: a page might use Grid for its overall header/sidebar/content skeleton, while the navigation links inside the header use Flexbox to space themselves out along that single row. Neither tool "replaces" the other; they solve different-shaped problems, and the gap property (spacing between items without fiddly margins) works identically in both.

Where this fits in CBSE Computer Science / Informatics Practices

CBSE's Computer Science and Informatics Practices syllabi for the senior grades include website development using HTML and CSS, and board-level and project-based questions increasingly expect layouts that don't rely on outdated <table>-based or float-based page structuring. Grid became reliably supported across all major browsers together around 2017, and Flexbox had already become dependable a couple of years before that — so any modern website-building question or project, at school level or in a first web-development internship, is expected to use these two tools rather than the older hacks. Getting comfortable now with tracing exactly how the browser computes pixel widths — as you did above with the fr unit and flex-grow arithmetic — is also good practice for the kind of precise, rule-following reasoning that later computational-thinking and algorithm questions reward.

Check yourself

  1. A container is display: flex with justify-content: space-between and three items of widths 100px, 150px, and 200px inside a 900px-wide container. Does justify-content change any of those three widths? What exactly does it change?
  2. A grid container is 1000px wide with grid-template-columns: 2fr 3fr; and no gap. Compute the pixel width of each column, showing your division.
  3. Same grid, but now add gap: 40px; between the two columns. Recompute both column widths.
  4. A flex container is 800px wide with gap: 20px and four children, each flex: 1. Compute each child's final width.
  5. A student writes grid-template-columns: 1fr 1fr; and then, in grid-template-areas, writes a row as "sidebar sidebar content" — three names for a two-column grid. Why will the browser reject or misbehave with this, and how should the row string be fixed to match a two-column grid where the sidebar and content sit side by side?

Answers. (1) No — justify-content never changes an item's own width; it only redistributes the leftover space between items along the main axis. Here, total item width is 100+150+200=450, so 900−450=450 pixels of free space get placed as a gap only between item 1–2 and item 2–3 (none before the first or after the last), each gap becoming 225px. (2) Total shares =2+3=5; each share =1000÷5=200px; columns are 400px and 600px. (3) Subtract the gap first: 1000−40=960; each share =960÷5=192px; columns become 384px and 576px (check: 384+576+40=1000). (4) Three gaps total 3×20=60px; remaining 800−60=740px split across 4 equal-grow items: 740÷4=185px each. (5) Every string in grid-template-areas must contain exactly as many names as there are declared columns — two columns need exactly two names per row. Writing three names for a two-column grid is invalid and the declaration is ignored by the browser. The fix is "sidebar content", matching the two declared columns one-to-one.

Summary

  • Without any layout CSS, HTML elements stack top-to-bottom in normal flow — Flexbox and Grid exist to override this deliberately, replacing older float/inline-block hacks.
  • display: flex arranges direct children along one axis (the main axis); justify-content distributes free space between items on that axis, align-items aligns items on the perpendicular cross axis.
  • flex: 1 means grow-1/shrink-1/basis-0%; free space (container width minus gaps minus basis) is split proportionally by grow factor — trace the subtraction-then-division every time, don't guess.
  • Flexbox is one-dimensional: wrapped rows never align their columns to each other. Grid is two-dimensional and can lock rows and columns together via grid-template-columns/grid-template-rows or a hand-drawn grid-template-areas map.
  • fr divides only the space left over after fixed-size tracks and gaps are subtracted — it is never a straight percentage of the full container.
  • grid-column: span n / grid-row: span n lets one item deliberately occupy multiple tracks, something proportional flex-grow cannot express.
  • Rule of thumb: single row or column of content-sized items → Flexbox. A coordinated multi-row, multi-column skeleton → Grid. Most real pages nest both.

Think About It

Think about this: How would you explain modern css: grid and flexbox layouts to a friend who has never seen a computer? What real-world analogy would you use? Imagine you had to build a system using these concepts — what would be your first step? Try this: before moving on, write down three things you learned and one question you still have.

← Data Structures: Organizing Information Like a ProBuilding a Simple Chat Application →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn