responsive layout with media queries

same HTML, different arrangement on mobile.

intermediate css layout
building on the 3-column grid tutorial — here's how it reflows for phones without touching the HTML.

the problem

a grid with grid-template-columns: 220px 1fr 230px looks great on a wide screen, but on a phone that's 375px wide, those fixed columns simply don't fit. we need a different arrangement for small screens — without duplicating the markup.

step 1 — write a media query

@media lets you write CSS that only applies below a certain screen width:

@media (max-width: 860px) {
  /* rules in here only apply when the screen
     is 860px wide or narrower */
}

step 2 — redefine the grid with named areas

instead of 3 fixed columns, switch to a 2-column grid and use grid-template-areas to name each region. this lets you place widgets anywhere, in any order, regardless of their original column:

@media (max-width: 860px) {
  .grid {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      "profile    calendar"
      "nowplaying nowplaying"
      "todo       updates"
      "welcome    welcome";
  }
}

each quoted string is one row. writing the same name twice in a row (like "nowplaying nowplaying") makes that widget span both columns full-width.

step 3 — assign each box to an area

#box-profile   { grid-area: profile; }
#box-calendar  { grid-area: calendar; }
#box-nowplaying{ grid-area: nowplaying; }
#box-todo      { grid-area: todo; }
#box-updates   { grid-area: updates; }
#box-welcome   { grid-area: welcome; }

step 4 — free the boxes from their original columns

on desktop, each box lives inside a .col-left, .col-center, or .col-right wrapper. on mobile we don't want those wrappers affecting layout at all — we just want their children to go straight to the named grid areas:

@media (max-width: 860px) {
  .col-left, .col-center, .col-right {
    display: contents;
  }
}
what display: contents does: it makes the wrapping <div> itself disappear from layout — as if you'd deleted the tag but kept its children. that's what allows a box originally written inside .col-left to land in a completely different area on mobile.

step 5 — reset desktop-only spacing tricks

the desktop layout uses flexbox tricks (like the last box in a column stretching to fill height) that don't make sense once boxes are placed by grid-area. reset them inside the same media query:

@media (max-width: 860px) {
  .col-left .box, .col-center .box, .col-right .box {
    margin-bottom: 0; /* spacing now comes from the grid's gap instead */
  }
  .col-left .box:last-child .box-body,
  .col-center .box:last-child .box-body,
  .col-right .box:last-child .box-body {
    height: auto;
    overflow-y: visible;
  }
}

step 6 — stack a second breakpoint for very small phones

you can nest additional media queries for even smaller screens, tightening padding, font sizes, and spacing further:

@media (max-width: 480px) {
  .page { padding: 12px; border-width: 10px; }
  .header-titles h1 { font-size: 24px; }
}

live preview

compare desktop and mobile side by side — both load the same HTML/CSS, only the iframe width changes:

desktop

mobile (375px)

below 860px, the grid drops from 3 fixed columns to 2 flexible ones, and grid-template-areas reassigns where each box lands — now playing becomes full-width, and welcome moves to the very bottom.

the boxes themselves don't move in the HTML! only their grid-area placement changes, which is why no markup duplication is needed to support both layouts.

result

the exact same HTML now renders as a clean 3-column layout on desktop, and reflows into a readable, thumb-friendly stack on mobile — with zero duplicated markup.