Lesson 3 of 107 min read

CSS Layout

Share:WhatsAppLinkedIn

What you'll build

By the end of this lesson you will have a two-column responsive layout: a wide main content area and a narrower sidebar, which collapses into a single column on phones. You will understand why things end up where they do, and how to fix them when they end up somewhere unexpected.

Concepts

The box model

Every element in CSS is a rectangular box. The box has four layers from inside out: content, padding, border, and margin.

.card {
  /* Content area: 300px × 150px */
  width: 300px;
  height: 150px;

  /* Space inside the border */
  padding: 16px;

  /* The visible edge */
  border: 2px solid #ccc;

  /* Space outside the border, pushes other elements away */
  margin: 24px;
}

By default, width means the content area only. So a box with width: 300px, padding: 16px, and border: 2px is actually 336px wide on screen (300 + 16×2 + 2×2). This surprises almost everyone the first time.

The fix is one global rule that most projects put at the top of their stylesheet:

*, *::before, *::after {
  box-sizing: border-box;
}

With border-box, width: 300px means the total rendered width is 300px, padding and border are counted inside that number, not added on top.

Flexbox

Flexbox is a one-dimensional layout system. You apply it to a container, and it lays out that container's direct children either in a row or a column.

.nav {
  display: flex;
  flex-direction: row;       /* default, so you can omit this */
  justify-content: space-between; /* space along the main axis (horizontal) */
  align-items: center;       /* align along the cross axis (vertical) */
  gap: 16px;                 /* space between children */
}
<nav class="nav">
  <a href="/">Home</a>
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
</nav>

The nav items will be spaced evenly across the full width, vertically centred. Flexbox is perfect for navbars, button groups, and anywhere you want items in a line with flexible spacing.

A few properties you will use constantly:

.container {
  display: flex;
  flex-wrap: wrap;     /* items wrap to next line instead of overflowing */
  gap: 12px;
}

.item {
  flex: 1;             /* grow and shrink equally, share available space */
  flex: 0 0 200px;     /* fixed 200px, do not grow or shrink */
  flex-grow: 2;        /* grow twice as much as siblings with flex-grow: 1 */
}

CSS Grid

Grid is a two-dimensional layout system. You define rows and columns on the container, then place children into cells. It is the right tool for page-level layouts.

.page-layout {
  display: grid;
  grid-template-columns: 1fr 300px; /* main column + fixed sidebar */
  grid-template-rows: auto 1fr auto; /* header, content, footer */
  gap: 24px;
  min-height: 100vh;
}

The fr unit means "fraction of available space". 1fr 300px gives the main column everything except 300px for the sidebar and the gap between them.

You can place items explicitly:

.header  { grid-column: 1 / -1; } /* span all columns */
.main    { grid-column: 1; }
.sidebar { grid-column: 2; }
.footer  { grid-column: 1 / -1; }

Or you can use the grid-template-areas shorthand, which is much more readable for page layouts:

.page-layout {
  display: grid;
  grid-template-columns: 1fr 300px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "main    sidebar"
    "footer  footer";
  gap: 24px;
}

.header  { grid-area: header; }
.main    { grid-area: main; }
.sidebar { grid-area: sidebar; }
.footer  { grid-area: footer; }

Responsive design with media queries

Media queries let you change CSS rules based on screen width. The standard approach is mobile-first: write styles for small screens first, then add media queries to override them for larger screens.

/* Mobile: single column, always */
.page-layout {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "header"
    "main"
    "sidebar"
    "footer";
  gap: 16px;
}

/* Tablet and above: two columns */
@media (min-width: 768px) {
  .page-layout {
    grid-template-columns: 1fr 280px;
    grid-template-areas:
      "header  header"
      "main    sidebar"
      "footer  footer";
    gap: 24px;
  }
}

/* Desktop: wider gap, bigger sidebar */
@media (min-width: 1200px) {
  .page-layout {
    grid-template-columns: 1fr 320px;
    gap: 32px;
  }
}

Always test with the browser's device toolbar (DevTools → responsive mode) after adding media queries.

Hands-on

Build a complete two-column responsive layout. Create layout.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Two-Column Layout</title>
  <style>
    *, *::before, *::after {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    body {
      font-family: system-ui, sans-serif;
      line-height: 1.6;
      color: #333;
      background: #f5f5f5;
    }

    /* ---- Page structure ---- */
    .page {
      display: grid;
      grid-template-columns: 1fr;
      grid-template-areas:
        "header"
        "main"
        "sidebar"
        "footer";
      gap: 16px;
      max-width: 1100px;
      margin: 0 auto;
      padding: 16px;
    }

    @media (min-width: 768px) {
      .page {
        grid-template-columns: 1fr 280px;
        grid-template-areas:
          "header  header"
          "main    sidebar"
          "footer  footer";
        gap: 24px;
        padding: 24px;
      }
    }

    /* ---- Regions ---- */
    .site-header {
      grid-area: header;
      background: #1a1a2e;
      color: white;
      padding: 16px 24px;
      border-radius: 8px;
    }

    .site-header nav {
      display: flex;
      gap: 24px;
      margin-top: 8px;
    }

    .site-header nav a {
      color: #a8b2d8;
      text-decoration: none;
    }

    .site-header nav a:hover {
      color: white;
    }

    .main-content {
      grid-area: main;
      background: white;
      padding: 24px;
      border-radius: 8px;
    }

    .sidebar {
      grid-area: sidebar;
      background: white;
      padding: 24px;
      border-radius: 8px;
    }

    .site-footer {
      grid-area: footer;
      background: #1a1a2e;
      color: #a8b2d8;
      padding: 16px 24px;
      border-radius: 8px;
      text-align: center;
    }

    /* ---- Card component using flexbox ---- */
    .card-grid {
      display: flex;
      flex-wrap: wrap;
      gap: 16px;
      margin-top: 16px;
    }

    .card {
      flex: 1 1 200px;
      background: #f0f4ff;
      border: 1px solid #d0d8ff;
      border-radius: 6px;
      padding: 16px;
    }

    .card h3 {
      margin-bottom: 8px;
    }
  </style>
</head>
<body>
  <div class="page">

    <header class="site-header">
      <h1>My Blog</h1>
      <nav>
        <a href="#">Home</a>
        <a href="#">Articles</a>
        <a href="#">About</a>
      </nav>
    </header>

    <main class="main-content">
      <h2>Latest Articles</h2>
      <div class="card-grid">
        <article class="card">
          <h3>Understanding Flexbox</h3>
          <p>A deep dive into one-dimensional CSS layouts.</p>
        </article>
        <article class="card">
          <h3>CSS Grid in Practice</h3>
          <p>Building real page layouts with grid areas.</p>
        </article>
        <article class="card">
          <h3>Responsive Design Tips</h3>
          <p>Writing mobile-first CSS that scales up gracefully.</p>
        </article>
      </div>
    </main>

    <aside class="sidebar">
      <h2>About</h2>
      <p>Writing about web development and design systems.</p>
      <h2 style="margin-top: 16px;">Tags</h2>
      <ul>
        <li>CSS</li>
        <li>JavaScript</li>
        <li>Design</li>
      </ul>
    </aside>

    <footer class="site-footer">
      <p>© 2026 My Blog</p>
    </footer>

  </div>
</body>
</html>

Open this in your browser and resize the window. Below 768px the sidebar drops below the main content and everything stacks into a single column. Above 768px you get the two-column layout. The cards inside main content wrap onto new rows when they do not fit, that is flex-wrap: wrap at work.

Open DevTools, click the device icon, and switch to iPhone view. The layout adapts automatically because of the min-width: 768px media query and the mobile-first defaults.

Common pitfalls

  • Forgetting box-sizing: border-box. Without it, adding padding or border makes elements wider than you intended, causing mysterious horizontal scrollbars. Put the reset at the very top of every stylesheet.
  • Using margin: auto on flex or grid children and wondering why it does nothing. Inside a flex container, margin: auto absorbs free space, this is actually useful! But margin: 0 auto for centering only works on block elements with a defined width. Know which context you are in.
  • Over-nesting selectors. Writing .page .main-content .card h3 { } is fragile and hard to override. Prefer single class selectors like .card-title { } and use classes liberally.
  • Mobile-last media queries. Writing desktop styles first and then using max-width queries to "fix" mobile is the old approach and leads to overriding more than you add. Write mobile styles as the default; use min-width to enhance for larger screens.
  • Using px for font sizes. Users can set a preferred base font size in their browser settings. If you use rem units (relative to the root font size), your text scales with their preference. 1rem equals the browser default (usually 16px). Use px for borders and spacing where you want fixed sizes.

What to try next

  1. Add a sticky navbar: set position: sticky; top: 0; z-index: 10; on the header and scroll the page to see it stay at the top.
  2. Change the card grid to CSS grid instead of flexbox: display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));. This creates a self-adjusting grid that fills available space without needing a breakpoint.
  3. Open DevTools, go to the Styles panel, and edit CSS rules live. Find the gap property and change it to see the layout update in real time. This is faster than editing a file and refreshing.

Test Your Knowledge

Take a quick quiz on this lesson

Start Quiz →

Prefer watching over reading?

Subscribe for free.

Subscribe on YouTube