@media queries
Apply completely different CSS rules when the viewport meets a condition.
Syntax
/* base — mobile first */ .card-grid { grid-template-columns: 1fr; } /* override at 480px and above */ @media (min-width: 480px) { .card-grid { grid-template-columns: 1fr 1fr; } } /* override at 720px and above */ @media (min-width: 720px) { .card-grid { grid-template-columns: 1fr 1fr 1fr; } }
Mobile-first: write the base styles for the smallest screen, then use min-width to override upward.
Demo 1 — Layout columns
The number of columns changes at breakpoints. clamp() cannot do this — it scales a value, it cannot restructure a grid.
.card-grid { grid-template-columns: 1fr; } @media (min-width: 480px) { grid-template-columns: 1fr 1fr; } @media (min-width: 720px) { grid-template-columns: 1fr 1fr 1fr; }
1 col · below 480px | 2 col · 480–719px | 3 col · 720px+
Demo 2 — Navigation structure
On mobile the nav stacks vertically. Above 540px it goes horizontal. This is a structural change — flex-direction has no concept of "scale smoothly".
.demo-nav { flex-direction: column; } /* mobile */ @media (min-width: 540px) { .demo-nav { flex-direction: row; } /* wider */ }
Below 540px: stacked | 540px+: horizontal
Demo 3 — Show and hide
Entirely showing or hiding an element is only possible with media queries. There is no clamp() equivalent.
.sidebar { display: none; } /* hidden on mobile */ @media (min-width: 600px) { .sidebar { display: block; } /* visible wider */ }
Sidebar hidden below 600px — visible above
When @media alone falls short
Media queries create discrete jumps. A heading that's 20px below 600px and suddenly 36px above it looks jarring at the boundary. For smooth value scaling, use clamp() instead — or both together.
/* jumpy — noticeable snap at the breakpoint */ h1 { font-size: 20px; } @media (min-width: 600px) { h1 { font-size: 36px; } } /* smooth — use clamp() for this instead */ h1 { font-size: clamp(20px, 4vw, 36px); }