Back to posts

100vh layout margin-top causing bottom spacing issues and solutions

Inside a container highly restricted by 100vh, the margin-top property of child elements may cause unnecessary bottom spacing and scrollbars.

This article will delve into the principles of this phenomenon and provide multiple solutions to help you build more robust and precise web layouts.


1. Problem Scenario: 100vh Container and margin-top Causing Overflow

  • Element A (Parent Container)
    • display: flex;
    • min-height: 100vh; (Minimum height of 100% viewport height)
  • Element B (A’s Flex Item)
  • flex: 1; (equivalent to flex-grow: 1; flex-shrink: 1; flex-basis: 0%;, making B take up all available space of A)
    • Implicit height: Since A has min-height: 100vh, and B is set to flex: 1, the calculated height of element B will extend to 100vh.
  • Element C (Child of B)
    • height: 100%; (will attempt to occupy the full height of parent element B, if the parent element does not explicitly set a height, this 100% will degrade to auto)
  • margin-top: 84px; (The key point)

Expected: I hope the content of element C starts to display 84px down from the top of its parent element B, and the entire layout can perfectly fill the 100vh viewport without producing any extra scrollbars.

Actual observation: When adding margin-top: 84px to element C, extra white space appears at the bottom of the page, often accompanied by a vertical scroll bar.

2. Why does margin-top cause this overflow?

This involves the interaction between the CSS box model and Flexbox layout.

  1. Parent element B’s height constraint: The effective height of element B (inherited from A’s 100vh via Flexbox) is fixed. This means the sum of B’s content area and padding is constrained within 100vh.

  2. margin-top is not included in the parent element’s height: When you set margin-top: 84px for element C, this margin tells the browser that the actual content and border area of C should be offset 84px downward from the top of its parent element B’s content area. However, this margin-top takes effect outside of C, and it is not included in the declared height of parent element B.

  3. Content overflow and container expansion: As a result, the bottom of element C will extend beyond the declared 100vh boundary of element B. Since ancestor elements (B and A) typically do not have overflow: hidden or overflow: scroll set, the browser will automatically expand their actual rendered height to accommodate the full content of C (including its margin). This additional 84px of space is the “gap” you see at the bottom of the page, often accompanied by a scrollbar.

3. Solution: Create space within 100vh

Our goal is: to have the content of element C start displaying 84px below the top of its parent container B, while ensuring the total height of parent container B remains strictly at 100vh, avoiding unnecessary page scrollbars.

This is the most direct, semantically appropriate, and common solution, especially suitable for scenarios where you want the content of child elements to leave space inside the parent element.

  1. Remove margin-top: 84px; from element C
  2. Add padding-top: 84px; to element B (parent container)
  3. Ensure element B has box-sizing: border-box; (It’s a good habit to set * { box-sizing: border-box; } in global styles).

CSS Example:

/* Element B */
.element-B {
    flex: 1;
    /* ...other styles */
    padding-top: 84px; /* create spacing inside the parent element */
box-sizing: border-box; /* Ensure padding is included in total height */
}

/* Element C */
.element-C {
/* Remove margin-top */
    height: 100%;
    /* ...other styles */
}

Principle: When box-sizing: border-box is in effect, padding is included in the element’s total height. Therefore, B’s 100vh height will include this 84px padding. The content area will start from 84px accordingly, but B’s total height remains 100vh, and there will be no overflow.

Advantages:

  • Clear Semantics: padding is used to create internal space between the content and the border of an element.
  • Stable Layout: The total height of the parent container remains unchanged, perfectly avoiding overflow and unnecessary scrollbars.
  • High Generalizability: Applies to most container types.

Handling the case where element C does not exist: If element C is conditionally rendered (e.g., controlled by v-if), you may want to apply this 84px top margin only when C exists. In this case, you need to combine JavaScript or your template engine:

  • Dynamically add class: When element C is rendered, dynamically add a specific class (e.g., has-child-spacing) to element B.
  • CSS:
.element-B.has-child-spacing {
    padding-top: 84px;
}

Option 2: Flexbox or Grid Layout Control (Advanced Option)

If parent container B is already a Flex or Grid container (or can be set as such), we can leverage the properties of these layout models to more precisely control spacing without causing overflow.

1. Using Flexbox and pseudo-elements (::before)

  • Remove margin-top: 84px; from element C
  • Set element B as a Flex container and add a pseudo-element with a height of 84px as top spacing.

CSS Example:

/* Element B */
.element-B {
    display: flex;
    flex-direction: column; /* Make child elements stack vertically */
    /* ...other styles, such as height: 100%; */
}

.element-B::before {
    content: ''; /* Pseudo-elements must have content */
    display: block; /* Ensure pseudo-elements can set height */
height: 84px; /* As spacing */
    flex-shrink: 0; /* Prevent compression when space is insufficient */
}

/* Element C */
.element-C {
/* Remove margin-top */
    height: 100%; /* or flex: 1; to let C take up the remaining space */
    /* ...other styles */
}

Principle: An invisible 84px high placeholder was created inside the Flex container B. Since B’s total height is 100vh, this 84px space is accounted for and does not cause overflow. Element C will follow immediately.

Advantages:

  • Precise control of spacing without causing overflow.
  • If B is already a Flex container, this approach is very natural.

Handling the case where element C does not exist: Similar to padding-top, the pseudo-element ::before is default present. If conditional styling is needed, it’s also required to dynamically add classes that control the pseudo-element’s styles through JS/template engine.

2. Using Grid Layout

  • Remove margin-top: 84px; from element C
  • Set element B as a Grid container and define a clear row height.

CSS Example:

/* Element B */
.element-B {
display: grid;
    /* Define two rows: first row 84px, second row takes up remaining space */
    grid-template-rows: 84px 1fr;
    /* ...other styles, such as height: 100%; */
}

/* Element C */
.element-C {
/* Remove margin-top */
grid-row: 2 / 3; /* Place element C in the second row */
    /* ...other styles */
}

Principle: The Grid layout precisely controls the height of each row. Even if the content of the second row is very little, the height of the first row (84px) will be retained. Since B’s total height is 100vh, 84px plus 1fr will precisely allocate this 100vh without overflow.

Advantages:

  • Extremely precise and powerful layout control.
  • Very suitable for complex grid layouts.

Handling the case where element C does not exist: The rows defined by Grid’s grid-template-rows always exist. Conditional styling also requires dynamic class control.

Summary

1、The key lies in understanding the different behaviors of margin-top and padding-top when the container has a fixed height: margin-top attempts to push the child element outside the parent’s boundary, forcing the parent element to expand; whereas padding-top creates a blank area inside the parent element, with its size constrained by the parent element’s own dimensions.

2、Understand the role of height: 100%; when the parent element does not explicitly set a height, this height will not take effect, meaning it is determined by the height of the child element’s content.

3、Understand that block formatting context (BFC) elements (flex/grid/overflow: hidden) do not have margin collapsing. It is recommended to use grid/flex for all layout purposes.

References