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;
}
}
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.