Responsive Container Heights: Why max() Beats calc() for Scroll Control
You have a landing page with absolutely positioned elements. The content needs around 1000px of vertical space, but on smaller screens (like a MacBook 14β), users canβt scroll to see everything.
A common quick fix looks like this:
.container {
min-height: calc(100vh + 300px);
}
This forces scroll by adding 300px to the viewport height. It works, but itβs not the right solution.
Why calc(100vh + 300px) Falls Short
The problem with this approach is that itβs always additive, regardless of screen size:
| Viewport Height | Result | Issue |
|---|---|---|
| 800px | 1100px | β Content fits |
| 982px | 1282px | β Content fits |
| 1200px | 1500px | β 200px of unnecessary scroll |
| 1440px | 1740px | β 440px of wasted space |
On larger screens, users are forced to scroll through empty space. Itβs a poor user experience.
The Better Approach: max()
Instead of blindly adding pixels, use max() to pick the larger value:
.container {
min-height: max(100vh, 1030px);
}
This reads as: βBe at least as tall as the viewport OR 1030px, whichever is greater.β
| Viewport Height | Result | Behavior |
|---|---|---|
| 800px | 1030px | Content fits, scroll enabled |
| 982px | 1030px | Content fits, minimal scroll |
| 1200px | 1200px | No scroll needed |
| 1440px | 1440px | No scroll needed |
Visual Comparison
Hereβs what happens on a large viewport (1200px):
calc(100vh + 300px) max(100vh, 1030px)
ββββββββββββββββββββ ββββββββββββββββββββ
β β β β
β Content β β Content β
β β β β
β β β β
ββββββββββββββββββββ€ 1200px ββββββββββββββββββββ 1200px
ββββββββββββββββββββ β
ββββ Empty Space βββ No wasted
ββββββββββββββββββββ scroll
ββββββββββββββββββββ 1500px
β
300px of forced
useless scroll
And on a small viewport (800px):
calc(100vh + 300px) max(100vh, 1030px)
ββββββββββββββββββββ ββββββββββββββββββββ
β β β β
β Content β β Content β
β β β β
β β β β
β β β β
ββββββββββββββββββββ 1100px ββββββββββββββββββββ 1030px
β β
Both work, Exactly what's
but calc adds needed
extra 70px
The difference is clear: calc() always adds unnecessary space, while max() adapts intelligently to each viewport.
How to Calculate the Fixed Value
The magic number (1030px in this example) comes from measuring your actual content:
Top offset of lowest element: 238px
+ Internal offset: 138px
+ Element height: 535px
+ Bottom breathing room: ~120px
βββββββββββββββββββββββββββββββββββββββββ
Total: ~1030px
Audit your layout, find the lowest point of your content, add some margin, and use that value.
Tailwind CSS Implementation
If youβre using Tailwind, both approaches work with arbitrary values:
<!-- β Avoid -->
<div class="min-h-[calc(100vh+300px)]">
<!-- β
Prefer -->
<div class="min-h-[max(100vh,1030px)]">
Key Takeaway
When dealing with fixed-position content that extends beyond the viewport:
- Donβt blindly add pixels to viewport height
- Do use
max()to set a content-aware minimum - Result: Scroll when needed, no scroll when not
The max() function gives you precise controlβensuring your content always fits while respecting larger viewports. Itβs a small change that significantly improves the user experience across all screen sizes.
Quick tip: If you find yourself frequently fighting scroll issues with absolute positioning, consider whether your layout could benefit from natural document flow using Flexbox or Grid instead.