CSS Flexbox and Grid: When to Use Each and How They Work Together
A practical guide to modern CSS layout — flexbox for one-dimensional alignment, grid for two-dimensional structure, and patterns for combining them in production components.
Master the clamp() function for responsive font sizes, gutters, and component dimensions — with formulas, real examples, and common mistakes that break layouts.
By codefunc
Hard breakpoints multiply fast. A heading that jumps from 24px to 32px at 768px and 40px at 1024px means three rules, three test viewports, and fragile maintenance. clamp() solves this with one line — a minimum, a preferred fluid value, and a maximum. The browser picks the middle value and constrains it. This guide teaches the math, the patterns, and the production pitfalls.
Bottom line: clamp(min, preferred, max) gives you fluid scaling between bounds. Use viewport units in the preferred value for responsive typography; always set sensible min/max so text never becomes unreadable.
clamp(MIN, PREFERRED, MAX)
Equivalent logic:
value = max(MIN, min(PREFERRED, MAX))
The browser computes PREFERRED (often using vw, %, or calc()), then clamps the result.
h1 {
font-size: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
| Viewport | Approximate result |
|---|---|
| 320px | ~1.75rem (hits minimum) |
| 768px | ~2.2rem (fluid middle) |
| 1440px | 3rem (hits maximum) |
No @media blocks required.
The community-standard formula (popularized by Utopia and fluid type calculators):
font-size: clamp(
1.125rem, /* min — 18px */
0.95rem + 0.5vw, /* fluid slope */
1.5rem /* max — 24px */
);
The preferred value is usually:
calc(baseRem + vwCoefficient * 1vw)
To derive coefficients from two viewport/font-size pairs, use a linear equation — or skip the algebra and use a clamp calculator that outputs ready-to-paste CSS.
:root {
--text-base: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.4vw, 1.375rem);
--text-xl: clamp(1.25rem, 1.05rem + 0.8vw, 1.75rem);
--text-2xl: clamp(1.5rem, 1.1rem + 1.5vw, 2.25rem);
}
body {
font-size: var(--text-base);
line-height: 1.6;
}
h1 {
font-size: var(--text-2xl);
line-height: 1.15;
}
Define a type scale as CSS variables once; reuse across components.
.section {
padding-block: clamp(3rem, 2rem + 4vw, 8rem);
padding-inline: clamp(1rem, 0.5rem + 2vw, 3rem);
}
.card-grid {
display: grid;
gap: clamp(1rem, 0.5rem + 1.5vw, 2.5rem);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}
.container {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
min() pairs well with clamp() — min() caps width; clamp() scales internal spacing.
| Technique | Best for |
|---|---|
clamp() |
Font sizes, spacing, gaps, hero padding |
| Media queries | Layout mode changes (1 col → 2 col) |
| Container queries | Component-level responsiveness inside cards |
clamp() + grid |
Fluid cards without breakpoint-specific columns |
Anti-pattern: 40 lines of @media only to nudge font-size by 2px. Replace with one clamp().
Good pattern: clamp() for continuous scaling + one media query when grid template actually changes.
/* Bad — can become microscopic on narrow screens */
font-size: clamp(0.5rem, 2vw, 2rem);
Always set a minimum readable size — typically 1rem for body, 1.5rem+ for headings.
If the middle term always evaluates above MAX, you only ever get MAX. If always below MIN, you only get MIN. The fluid range only exists when the preferred value crosses between min and max at realistic viewports.
Fluid large headings need tighter line-height:
h1 {
font-size: clamp(2rem, 1.5rem + 2vw, 3.5rem);
line-height: 1.1;
}
clamp(18px, 1rem + 2vw, 32px) works but hurts maintainability. Prefer rem for accessibility (respects user font settings).
clamp() respects user zoom in modern browsers when using relative units. Avoid locking critical text in fixed px minimums that block scaling.
.hero-title {
font-size: clamp(
2rem,
calc(1.5rem + 1.5vw + 0.5vh),
4rem
);
}
Adding a small vh term can balance landscape mobile — use sparingly; vh on mobile has historical quirks (address bar). Test on real devices.
@theme {
--font-size-display: clamp(2.5rem, 1.8rem + 2.5vw, 4.5rem);
--space-section: clamp(4rem, 2rem + 5vw, 10rem);
}
Tailwind v4's CSS-first config aligns naturally with fluid tokens.
.card {
padding: clamp(1rem, 0.75rem + 1vw, 1.75rem);
border-radius: clamp(0.5rem, 0.4rem + 0.3vw, 1rem);
}
.card-title {
font-size: clamp(1.125rem, 1rem + 0.5vw, 1.5rem);
}
clamp() is computed at layout time — negligible cost compared to JavaScript resize listeners. Do not use JS resize handlers for font scaling when CSS can do it. Removing listeners improves INP (Interaction to Next Paint).
clamp() is supported in all evergreen browsers and Safari 13.4+. For legacy support (rare in 2026 greenfield), provide a fallback:
h1 {
font-size: 2rem;
font-size: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
text-wrap: balance)Use these codefunc tools alongside this guide:
Replace stepped font-size media queries with a clamp() type scale. Pair fluid typography with fluid spacing tokens. Use calculators to generate the middle calc() term. Keep layout mode switches in media or container queries. Test extremes — minimum width and maximum zoom — not just your laptop viewport.
A practical guide to modern CSS layout — flexbox for one-dimensional alignment, grid for two-dimensional structure, and patterns for combining them in production components.
Choose transitions vs @keyframes, pick units that scale, and build reusable motion snippets — with performance and accessibility rules that keep UI snappy.
Build practical gradient and shadow design tokens, pick easing curves that feel right, and avoid paint-heavy effects that tank scroll performance.
Find a developer tool