Responsive Container Heights: Why max() Beats calc() for Scroll Control

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 HeightResultIssue
800px1100pxβœ“ Content fits
982px1282pxβœ“ Content fits
1200px1500pxβœ— 200px of unnecessary scroll
1440px1740pxβœ— 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 HeightResultBehavior
800px1030pxContent fits, scroll enabled
982px1030pxContent fits, minimal scroll
1200px1200pxNo scroll needed
1440px1440pxNo 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:

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.