CSS flex: prevent overflow of grandchild (i.e., force scroll)

Viewed 154

Given the following:

<main>
  <h1>Hello World</h1>
  <nav><button>Home</button> - <button>Contact</button> - <button>About</button></nav>
  <br>
  <div class=wrapper>
    <b>List header</b>
    <ul></ul>
  </div>
</main>

How to force ul to scroll, and not overflow main nor .wrapper? I.e., the scrollbar should appear for ul, not main, nor .wrapper. Without hardcoding values.

I tried the following CSS but it fails. It would work if I was trying to scroll .wrapper, but I'm trying to scroll its child.

main {
  display: flex;
  flex-direction: column;
  max-height: 100vh;
}
.wrapper {
  flex: 1;
}
ul {
  overflow: auto; /* How to force this to scroll? */
}

Codepen: https://codepen.io/mustafa0x/pen/oNLXyxm

1 Answers

block elements, by default, grow as much as they are allowed to. This is why your ul element doesn't display any bars.

Try setting a height, like height: 150px and you will see some scrollbars.

// Lorem ipsum
const $ = q => document.querySelector(q);
const url = 'https://baconipsum.com/api/?type=all-meat&sentences=100';
fetch(url).then(r => r.json()).then(data => {
   $('ul').innerHTML = data[0].split('. ').map(l => '<li>' + l).join('\n');
});
main {
  display: flex;
  flex-direction: column;
  max-height: 100vh;
  
  /* Styling */
  max-width: 600px;
  margin: 0 auto;
  text-align: center;
}
.wrapper {
  flex: 1;
}
ul {
  overflow: auto; /* How to force this to scroll? */
  height: 150px;
  /* Styling */
  text-align: left;
}
<main>
  <h1>Hello World</h1>
  <nav><button>Home</button> - <button>Contact</button> - <button>About</button></nav>
  <br>
  <div class=wrapper>
    <b>List header</b>
    <ul></ul>
  </div>
</main>

Since you are not allowed to use fixed values, and also not allowed to modify the HTML, what you can do is to introduce another flex.

flex items, by default, have min-height: auto. Which means a flex item cannot be smaller than the size of its content.

To override that, you need to set min-height: 0 on your flex item, in this case .wrapper. That will cause the .wrapper to reach until the end of the screen.

But still, the ul element will keep increasing. To prevent that, without using fixed units, you need to convert the .wrapper into a column-directed flexbox as well.

// Lorem ipsum
const $ = q => document.querySelector(q);
const url = 'https://baconipsum.com/api/?type=all-meat&sentences=100';
fetch(url).then(r => r.json()).then(data => {
   $('ul').innerHTML = data[0].split('. ').map(l => '<li>' + l).join('\n');
});
main {
  display: flex;
  flex-direction: column;
  max-height: 100vh;
  max-width: 600px;
  margin: 0 auto;
  text-align: center;
}
.wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
ul {
  overflow: auto; /* How to force this to scroll? */
  text-align: left;
}
<main>
  <h1>Hello World</h1>
  <nav><button>Home</button> - <button>Contact</button> - <button>About</button></nav>
  <br>
  <div class=wrapper>
    <b>List header</b>
    <ul></ul>
  </div>
</main>

Related