As a full-stack developer, one of the most common HTML/CSS issues I run into is trying to stretch a div element to match the full viewport height using height: 100%. It rarely works as simply as expected.

In this comprehensive guide, we’ll unpack the inner workings of percentage-based heights, analyze why height: 100% fails on divs, and explore cross-browser solutions to build truly full height layouts.

The Complex World of Percentage Heights

To understand why height: 100% breaks, we first need to take a deeper look at how browsers calculate percentage-based heights. This ends up being a complex topic that intersects box model rendering, CSS cascade behaviors, and other quirky browser logic.

The Browser Box Model

In CSS, every element generates a rectangular box that consists of:

  • Content: The content inside the element, like text or images
  • Padding: Space around content, inside of any borders
  • Border: A border around padding and content
  • Margin: Space outside of border, separating from other elements

This is conceptualized in the box model diagram:

Box model diagram showing content, padding, border and margin areas

The total width and height of an element is calculated by combining all parts of the box model together.

By default, browser styling is set so that nested child elements stretch to match the height of their parent container. This allows consistently sizing elements to fill available space.

How Percentage Heights Interact with Box Model

When you apply a percentage height to an element, the percentage is calculated based on the height of the content area of the parent element.

So if you set a div to height: 50%, it will be 50% the height of its parent‘s content box area specifically. Padding, border and margin on the parent do not influence the percentage height calculations.

This becomes very important when working with the default html and body elements.

html and body Have No Default Height

Most developers expect that the root html and body elements make up the full height that percentage heights should calculate against.

But in reality, browsers do not apply a default height to these elements. So by default both html and body have a content height of 0.

When you inspect them in DevTools, they appear to stretch the full viewport height. But that is because the browser forces them to contain their child elements. There is no explicit height set on the elements themselves.

This means any child elements have no absolute value to calculate percentages against. And hence where problems begin emerging when we try applying height: 100%.

Why Height 100% Fails on Child Divs

Armed with an understanding of box models and height calculations, we can now specifically analyze the issue with div elements failing to stretch 100% height, even on the root body element itself.

Height Inheritance in DOM Tree

Every HTML document generates a DOM tree structure representing parent-child element relationships:

html
└── body
    └── div
        └── child elements

Height and width are inherited properties in the DOM tree. By default, child elements receive height/width values from their parent container.

So when you set a div to height: 100%:

  1. The div first checks if its parent body element has an explicit height set.

  2. If no, the div then adopts the body height value of 0.

  3. So the div in turn sets its own height to 0.

  4. With a height of 0, the div collapses down to just the height of its content.

This causes the visual failure of height: 100% not expanding the div further.

The key culprit is that root elements like body have no standard height by default for child elements to inherit against.

Browser Rendering Order Quirks

To examine why the body has no height set, we need to also understand browser rendering order.

The steps a browser takes to render a page are roughly:

  1. Parse HTML to generate DOM tree
  2. Apply CSS rules
  3. Calculate layout for boxes
  4. Paint pixels to screen

The important callout is that CSS is applied before final layout is calculated.

So when the browser encounters our height: 100% rules on child elements, initial layout has not yet been determined for parent sizes. Under normal CSS cascade rules, the child elements would just inherit the parent heights once layout occurs.

However, browser rendering introduces one big quirk here – they do not let child elements that have percentage heights actually inherit from parent elements correctly in the final layout calculations.

This seems to be an optimization so that browsers don‘t get stuck in recursive layout loops. However, it leads to the browser kind of “forgetting” to apply inherited height values down the DOM tree if percentages are used.

In essence, browser rendering order causes child percentage heights to get incorrectly resolved to 0 before final layout. This manifests as the well known issue of percentage-height children appearing not to inherit from their parents.

Default Document Flow Behavior

The last piece is understanding default document flow behavior. By default, elements stack vertically following normal flow. Parents stretch to contain child elements.

So in our common scenario:

  1. The div first sets its height to 0 because of having no resolved percentage.
  2. As parents stretch to contain children, the body stretches tall enough to fit the div.
  3. But – since the div has already incorrectly set its height to 0, it does not then re-inherit any new expanded height from body.
  4. Result – div remains collapsed to its content‘s height even as body grows taller.

This explains exactly why height: 100% fails on child elements.

Together with box model calculations and browser rendering order, we end up with percentage-based inheritance breakages.

The good news is that with the right CSS techniques, we can overcome these issues consistently.

Building Full Height Layouts

While complex under the hood, in practice achieving full-viewport-height layouts has simple solutions that address the percentage height pitfalls covered above.

Explicitly Set Body Height

The straightforward fix is to just manually set an explicit height on body itself:

body {
  height: 100vh; 
}
  • Viewport units like vh are equal to 1% of viewport height.

By manually forcing the body to the viewport height:

  1. We give child percentage heights a resolved absolute value to calculate against.
  2. Children can correctly set their heights relative to this parent height.

So now a child div respects the manually set parent height:

.full-height {
  height: 100%; /* 100% of parent */
}

Setting explicit heights on parents is the simplest fix for any percentage-based height issues.

Set Viewport Heights on html and body

For best cross-browser support, set height: 100% on both root elements:

html, 
body {
  height: 100%; 
}
  • Use min-height as backup too.

This ensures every document level from html down inherits a percent value that relatates to the viewport height all the way through. Fixes can then work reliably everywhere:

.full-height {
  height: 100%; 
  min-height: 100%;
}  

Flexbox Stretch Behavior

An alternative is using Flexbox, which changes how heights get inherited:

body {
  display: flex; 
  flex-direction: column;  
}
  • Set flex properties on parents
  • Children stretch regardless of percentages

Flexbox changes document flow behavior so that percentage-based children can stretch properly again.

However, Flexbox has less browser support than simple explicit height fixes. So I recommend combining both solutions:

html, body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column; 
}

This gets you flexible stretching behavior plus full legacy browser support.

Grid and Multi-Column Layouts

Setting explicit heights also unlocks adding CSS grid and column interfaces:

body {

  display: grid;
  grid-template-columns: 1fr 1fr;

  height: 100vh;

}

With a parent height fix in place, grid and column interfaces now work reliably:

Example screen shot displaying 2 column grid layout stretched full height

So fixing parent height issues should be the first step to unlock many modern layout interfaces.

Watch Out for Overflow Clipping

One final gotcha – make sure overflow is allowed on parent elements:

html, body {
  height: 100%;

  /* Add this */
  overflow-y: auto; 
}

Browsers clip child elements at parent container bounds if overflow is hidden. Allow scrolling overflow to avoid clipping at percentages less than 100%.

Browser Support and Compatibility

The parent height solutions work excellent in modern browsers. Explicitly setting document element heights is the most robust and backwards-compatible approach.

percentage heights has 93.84% global usage.

Browser Supported
Chrome Full standards support
Firefox Full standards support
Safari Full standards support
Edge Full standards support
IE 11 Works with fallback min-height

Flexbox is supported on all major browsers, but has lower usage at 89.6% due to missing legacy IE support.

So use optional Flexbox stretching enhancements carefully depending on your browser support requirements.

Conclusion

This guide took you deep on the internals of height calculations and browser rendering behaviors. While the concepts are complex under the hood, we discovered simple fixes:

  1. Set explicit heights on parent elements
  2. Allow overflow scrolling

These solutions reliably address the common issue of child elements not inheriting percentage-based heights properly.

By mastering percentage-based layouts, you can build robust, flexible interfaces stretching edge to edge – on any browser.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *