Question

Equal Height Columns in CSS: Make Navigation Match Content Height

htmlcss

Question

Given this page structure:

<div id="header"></div>

<div id="main">
  <div id="navigation"></div>
  <div id="content"></div>
</div>

<div id="footer"></div>

The #navigation element appears on the left and #content appears on the right. The content is loaded dynamically with PHP, so its height varies from page to page.

How can #navigation be made the same height as #content without assigning a fixed height to #main?

Short Answer

You will learn why height: 100% does not work when a parent has an automatic height, and how Flexbox can create side-by-side columns that automatically share the height of the tallest column. This is the standard modern solution for a dynamic navigation and content layout.

Concept

A percentage height depends on a parent with a definite, explicitly calculated height.

For example, this does not usually achieve equal columns:

#navigation {
  height: 100%;
}

If #main has its default height: auto, its height is determined by its children after they are laid out. CSS therefore has no fixed number from which 100% can be calculated.

The real requirement is not “make navigation 100% high”; it is “make two columns share the height of the tallest column.” Flexbox was designed for this kind of layout.

When #main is a flex container with a horizontal direction, its direct children become flex items. Flex items stretch along the cross axis by default. In a row layout, the cross axis is vertical, so both #navigation and #content stretch to the height of the tallest item.

If dynamic PHP output makes #content taller, #main grows to contain it and the navigation stretches with it automatically.

Mental Model

Think of #main as a tray holding two containers side by side.

  • #content is a container whose amount of material changes on every page.
  • #navigation is another container beside it.
  • The tray grows as tall as the tallest container.
  • With Flexbox, the tray tells both containers to extend from the top to the bottom of that shared height.

By contrast, height: 100% is like asking for a container that is “100% of the tray” before the tray's final height has been decided.

Take Quiz

Syntax and Examples

Make the parent a flex container:

#main {
  display: flex;
}

#navigation {
  width: 220px;
  background: #e8eef7;
}

#content {
  flex: 1;
  padding: 1rem;
  background: #ffffff;
}
<div id="main">
  <nav id="navigation">
    <a href="/home">Home</a>
    <a href="/about">About</a>
  </nav>

  <main id="content">
    <h1>Page content</h1>
    <p>This content can be short or very long.</p>
  </main>
</div>

display: flex places the direct children of #main in a row by default.

  • width: 220px gives the navigation a fixed column width.
  • flex: 1 tells content to use the remaining horizontal space.
  • The default align-items: stretch makes both columns equal in height.

You do not need height: 100% on either child.

Step by Step Execution

Consider this layout:

#main {
  display: flex;
}

#navigation {
  width: 200px;
  background: lightsteelblue;
}

#content {
  flex: 1;
  background: ivory;
}
<div id="main">
  <aside id="navigation">Navigation links</aside>
  <section id="content">
    <h2>Article</h2>
    <p>A long article adds enough text to make this section 700 pixels tall.</p>
  </section>
</div>

Execution flow:

  1. The browser sees display: flex on #main.
  2. Its direct children, #navigation and #content, become flex items.
  3. Because no flex-direction is specified, the items are arranged in a horizontal row.
  4. #navigation receives a width of 200px.
  5. #content expands into the remaining available width because of flex: 1.
  6. The long article makes #content 700 pixels tall.
  7. #main becomes 700 pixels tall to contain its tallest child.
  8. Flexbox stretches #navigation to the same 700-pixel height.

If a different PHP page produces more or less content, these measurements are recalculated automatically.

Real World Use Cases

Equal-height columns are useful whenever one panel should visually continue beside a variable-height panel:

  • A documentation site with a left table of contents and a long article.
  • An admin dashboard with a sidebar beside a report or form.
  • A product listing page with filter controls beside dynamically loaded results.
  • A help center with category navigation beside an article.
  • A chat interface with a conversation area beside user or channel information.
  • An application shell where a sidebar background should reach the bottom of the current page content.

The content can come from PHP, a database, an API, a CMS, or JavaScript. CSS only responds to the final rendered content height; it does not matter how the HTML was generated.

Take Quiz

Real Codebase Usage

In a real project, use semantic HTML and let the layout container manage the columns:

<main class="page-layout">
  <aside class="sidebar" aria-label="Site navigation">
    <!-- navigation -->
  </aside>

  <article class="page-content">
    <!-- server-rendered content -->
  </article>
</main>
.page-layout {
  display: flex;
  align-items: stretch;
  gap: 1.5rem;
}

.sidebar {
  flex: 0 0 16rem;
  background: #f4f6f8;
}

.page-content {
  min-width: 0;
  flex: 1;
}

@media (max-width: 700px) {
  .page-layout {
    flex-direction: column;
  }
}

Common project patterns:

  • Use aside for complementary navigation and main or article for the primary content.
  • Use flex: 0 0 16rem when the sidebar should neither grow nor shrink and should remain 16rem wide.
  • Add min-width: 0 to a flexible content panel when it may contain long text, tables, code blocks, or other wide content. It allows the panel to shrink instead of overflowing the row.
  • Use a media query to stack columns on small screens.
  • Apply the sidebar background or border directly to the stretched sidebar, rather than trying to calculate its height in PHP or JavaScript.

Common Mistakes

Using height: 100% with an auto-height parent

/* Usually not a solution */
#navigation {
  height: 100%;
}

100% needs a definite parent height. Since #main grows according to its children, this creates a circular requirement rather than equal-height columns.

Use Flexbox instead.

Forgetting to make the parent a flex container

#navigation {
  flex: 0 0 200px;
}

Flex properties only affect an element when its parent has display: flex or display: inline-flex.

#main {
  display: flex;
}

Setting align-items: flex-start

#main {
  display: flex;
  align-items: flex-start;
}

This prevents vertical stretching, so each item stays only as tall as its own content. Omit this declaration or use align-items: stretch.

Using JavaScript to measure and copy heights

// Avoid for ordinary layouts
navigation.style.height = `${content.offsetHeight}px`;

This is fragile: fonts, images, window resizing, and later content updates can change heights. CSS layout tools should handle visual layout whenever possible.

Applying Flexbox to the wrong element

Only direct children of a flex container become flex items. If the sidebar is nested inside another wrapper, apply display: flex to the parent that directly contains the two intended columns.

Expecting equal height after stacking on mobile

When flex-direction: column is used, the cross axis becomes horizontal. The panels normally no longer share equal vertical height, which is generally the desired mobile layout.

Comparisons

TechniqueBest useEqual dynamic column heights?Notes
height: 100%A child inside a parent with an explicit heightNo, not with an auto-height parentPercentage heights need a definite containing height.
FlexboxOne-dimensional rows or columnsYesBest default for a sidebar and content layout.
CSS GridPage layouts with rows and columnsYesUseful when you also need structured rows or multiple columns.
float: leftMaintaining legacy layoutsNot naturallyFloats were common before Flexbox but need workarounds for equal backgrounds.
JavaScript height calculationRare, special visual effectsPossibleAvoid for standard layout because it must be updated as content changes.

A CSS Grid version is also valid:

#main {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
}

Grid items in the same row stretch to the row height by default. Choose Flexbox for a simple two-column layout; choose Grid when the overall layout needs explicit rows and columns.

Take Quiz

Cheat Sheet

/* Dynamic equal-height sidebar and content */
.layout {
  display: flex;
  /* Default: flex-direction: row */
  /* Default: align-items: stretch */
}

.sidebar {
  flex: 0 0 220px; /* grow | shrink | basis */
}

.content {
  min-width: 0;
  flex: 1;
}
  • Put display: flex on the shared parent.
  • Equal vertical stretching applies to the parent's direct children.
  • Do not rely on height: 100% when the parent height is auto.
  • The default align-items: stretch creates equal column heights in a row.
  • Avoid align-items: flex-start if you want stretched columns.
  • Use flex-direction: column to stack the layout on narrow screens.
  • Use gap for spacing between columns; it does not stop equal-height stretching.

FAQ

Why does height: 100% not make my sidebar as tall as my content?

A percentage height is calculated from the parent's definite height. When the parent has height: auto, it gets its height from its children, so there is no explicit height for 100% to use.

Does Flexbox require a fixed height on the parent?

No. The parent can grow naturally based on dynamic content. Flexbox then stretches the other flex items to the height of the tallest item.

Do I need to add align-items: stretch?

Usually no. stretch is the default value for align-items in a flex container. Add it explicitly only when it improves readability or overrides another rule.

Will this work when PHP changes the content length?

Yes. PHP generates HTML before the page is sent to the browser. Once the browser renders that HTML, Flexbox calculates the layout using the actual content height.

Can I use CSS Grid instead of Flexbox?

Yes. A two-column grid also gives items in the same row a shared row height. Flexbox is often simpler for one sidebar and one content area.

Why is my sidebar still not stretching?

Check whether the parent has display: flex, whether the sidebar is a direct child of that parent, and whether align-items: flex-start is overriding the default stretch behavior.

Should I use JavaScript to make the heights equal?

Not for a standard two-column layout. Flexbox or Grid handles dynamic height changes more reliably and with less code.

Take Quiz

Mini Project

Description

Build a responsive documentation-page layout with a left navigation sidebar and a right article. The article has a variable amount of text, so the sidebar background must automatically extend to the same height without a fixed parent height or JavaScript.

Goal

Create a two-column Flexbox layout in which a fixed-width sidebar matches the dynamic article height and stacks vertically on small screens.

Requirements

Use a parent container that holds a sidebar and an article content area.

Make the sidebar 220px wide on larger screens.

Make the content area fill the remaining horizontal space.

Ensure the sidebar background reaches the full height of the longest column.

Stack the sidebar above the content at viewport widths of 700px or less.

Take Quiz

Keep learning

Related questions

View more