65

I have a webapp with which I'm using flexbox for layout.

I'm trying to both fill the screen (it's an app, not a document), and as far as possible to not specify any fixed widths or heights as the content could be all sorts of things (Full fluid layout! The dream!)

So I need fluid height, full width headers and footers, and then a main panel in the middle filling the remaining vertical space, divided into columns, each of which scrolls when too high, and where the width of each non-primary column should shrink to fit its content, and a primary column which uses up the remaining space.

I am so close, but have had to resort to explicitly sizing the non-main columns - I believe that flex-basis: content; is supposed to do this but isn't supported by browsers yet.

Here's a minimal demo showing fixed size columns:

var list = document.querySelector('ul')

for (var i = 0; i < 100; i++) {
  var li = document.createElement('li')
  li.textContent = i
  list.appendChild(li)
}
html,
body {
  height: 100%;
  width: 100%;
  margin: 0;
}
body {
  display: flex;
  flex-direction: column;
}
main {
  display: flex;
  flex-direction: row;
  overflow: hidden;
}
main > section {
  overflow-y: auto;
  flex-basis: 10em;
  /* Would be better if it were fluid width/shrink to fit, unsupported: */
  /* flex-basis: content; */
}
main > section:last-child {
  display: flex;
  flex: auto;
  flex-direction: column;
}
main > section:last-child > textarea {
  flex: auto;
}
<header>
  <h1>Heading</h1>
</header>

<main>
  <section>
    <h1>One</h1>
    <ul>
    </ul>
  </section>

  <section>
    <h1>Two</h1>
  </section>

  <section>
    <header>
      <h1>Three</h1>
    </header>
    <textarea></textarea>
    <footer>
      <p>Footer</p>
    </footer>
  </section>
</main>

<footer>
  <p>Footer</p>
</footer>

Which looks like this - I want columns One and Two to shrink/grow to fit rather than being fixed:

enter image description here

My question is, is there a CSS-only workaround for flex-basis: content, or an alternative way to realise this goal?

I can possibly live with fixing the column sizes as above, or using javascript, but I HAVE A DREAM DAMN IT.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
nrkn
  • 1,752
  • 3
  • 15
  • 24

3 Answers3

67

I want columns One and Two to shrink/grow to fit rather than being fixed.

Have you tried: flex-basis: auto

or this: flex: 1 1 auto, which is short for:

  • flex-grow: 1 (grow proportionally)
  • flex-shrink: 1 (shrink proportionally)
  • flex-basis: auto (initial size based on content size)

or this:

main > section:first-child {
    flex: 1 1 auto;
    overflow-y: auto;
}
    
main > section:nth-child(2) {
    flex: 1 1 auto;
    overflow-y: auto;
}
    
main > section:last-child {
    flex: 20 1 auto;
    display: flex;
    flex-direction: column;  
}

revised demo

Related:

roapp
  • 530
  • 6
  • 17
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Yes - the former makes the columns too small, cutting off some of their content, the latter distributes space evenly between all three columns – nrkn Nov 06 '15 at 00:30
  • Then you can apply the `flex` property to each column individually with unique values. I revised my answer and added a modified fiddle demo. – Michael Benjamin Nov 06 '15 at 00:36
  • 2
    Are we any closer to the dream? – Michael Benjamin Nov 06 '15 at 00:44
  • Thanks so much, but it is still essentially fixed width - just proportionately to the parent width rather than using explicit units - I believe it to be a better alternative to my explicit flex-basis, but it still means unused space/cutting off content in many cases – nrkn Nov 06 '15 at 00:46
  • You have no `padding` in the column, which would create a buffer between the content and the edges. http://jsfiddle.net/4ovu226m/2/ – Michael Benjamin Nov 06 '15 at 00:49
  • You can see what I expect to happen if you take out the vertical fitting: http://jsfiddle.net/2w157dyL/ Now it behaves exactly how I'd like it - I just don't seem to be able to combine the two! – nrkn Nov 06 '15 at 01:19
  • 9
    So maybe what you're looking for is `flex: 0 0 auto`. Meaning, *don't grow*, *don't shrink*, *size width to fit content*: http://jsfiddle.net/4ovu226m/3/ – Michael Benjamin Nov 06 '15 at 01:30
  • 1
    I have it now - your comment about padding led me to try something which enlightened me as to what the *real* problem was - see answer below :) – nrkn Nov 06 '15 at 01:31
  • Couldn't get this to work with flex-grow: 0 and flex-shrink: 0 until i realized my component library's grid (Semantic UI React) had a rogue css rule setting the width https://medium.freecodecamp.org/flexboxs-flex-basis-explained-83d1a01413b7 – Roman Scher May 23 '19 at 20:53
1

It turns out that it was shrinking and growing correctly, providing the desired behaviour all along; except that in all current browsers flexbox wasn't accounting for the vertical scrollbar! Which is why the content appears to be getting cut off.

You can see here, which is the original code I was using before I added the fixed widths, that it looks like the column isn't growing to accomodate the text:

http://jsfiddle.net/2w157dyL/1/

However if you make the content in that column wider, you'll see that it always cuts it off by the same amount, which is the width of the scrollbar.

So the fix is very, very simple - add enough right padding to account for the scrollbar:

http://jsfiddle.net/2w157dyL/2/

  main > section {
    overflow-y: auto;
    padding-right: 2em;
  }

It was when I was trying some things suggested by Michael_B (specifically adding a padding buffer) that I discovered this, thanks so much!

Edit: I see that he also posted a fiddle which does the same thing - again, thanks so much for all your help

nrkn
  • 1,752
  • 3
  • 15
  • 24
  • 2
    Yes, that was the basis of my `padding` suggestion, to create some space between the content and the edges. I'm glad the issue is resolve. Nice work. – Michael Benjamin Nov 06 '15 at 01:38
  • Yeah I got so confused. I thought that flexbox worked - well, the way that flexbox *does* work - then when I saw that column getting cut off like that, I started questioning my mental model, made some stupid assumptions - it must have been very confusing for you trying to understand what I was on about, sorry! – nrkn Nov 06 '15 at 02:00
  • I was a bit confused. The question wasn't clear. But I understand that you weren't sure what the problem was, so it was difficult to focus the question on anything specific. – Michael Benjamin Nov 06 '15 at 02:05
  • After reading your answer, I actually searched to see if there was a way to render a scrollbar *outside* a `div`, as opposed to inside. But nothing I found beats the padding solution, which is simple and effective. – Michael Benjamin Nov 06 '15 at 02:07
  • 1
    Yeah I looked into it too - padding seems the best way to go – nrkn Nov 06 '15 at 03:33
0

display: inline-flex; gives the expected result.

Adriaan
  • 17,741
  • 7
  • 42
  • 75