Asked  7 Months ago    Answers:  5   Viewed   51 times

I am trying to have a number of items underneath each other in a container with a set height. Items will then carry on next to each other if there's no space left.

This is the idea:

I am trying to achieve this using flexbox, a container with a set height, direction is set to column and flex-wrap is wrap:

The issue is that there are wide gaps between the columns.

enter image description here

I tried setting both justify-content and align-items to flex-start, but that is probably the default value.

Is there any way to solve this?

Here is the code:

* {
  box-sizing: border-box;
}
body {
  font-family: sans-serif;
}
.container {
  display: flex;
  flex-wrap: wrap;
  height: 300px;
  flex-direction: column;
  background-color: #ccc;
}
.items {
  width: 100px;
  height: 100px;
  margin: 10px;
  background-color: tomato;
  color: white;
  font-size: 60px;
  font-weight: bold;
  text-align: center;
  padding: 15px;
}
<div class="container">
  <div class="items">1</div>
  <div class="items">2</div>
  <div class="items">3</div>
  <div class="items">4</div>
  <div class="items">5</div>
</div>

codepen

 Answers

21

An initial setting of a flex container is align-content: stretch.

This means that multiple lines of flex items will be distributed evenly along the cross axis.

To override this behavior, apply align-content: flex-start to the container.


When you're working in a single-line flex container (i.e., flex-wrap: nowrap), the properties to use to distribute space along the cross axis are align-items and align-self.

When you're working in a multi-line flex container (i.e., flex-wrap: wrap) – like in the question – the property to use to distribute flex lines (rows / columns) along the cross axis is align-content.

From the spec:

8.3. Cross-axis Alignment: the align-items and align-self properties

align-items sets the default alignment for all of the flex container’s items, including anonymous flex items. align-self allows this default alignment to be overridden for individual flex items.

8.4. Packing Flex Lines: the align-content property

The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.

The align-content property takes six values:

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • stretch

Here's the definition for stretch:

stretch

Lines stretch to take up the remaining space. If the leftover free-space is negative, this value is identical to flex-start. Otherwise, the free-space is split equally between all of the lines, increasing their cross size.

In other words, align-content: stretch on the cross axis is similar to flex: 1 on the main axis.

Tuesday, June 1, 2021
 
mnagel
answered 7 Months ago
57

The most common way, which most framework use to solve that, is to set a top margin on the items (tag) instead, and then compensate that with a negative margin on the items parent (tags)

* {
  margin: 0;
  padding: 0;
}

html, body {
  box-sizing: border-box;
}

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  list-style-type: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  margin-top: -5px;                 /*  compensate top  */
}

.tag {
  padding: 5px;
  background-color: #76FF03;
  margin: 5px 5px 0 0;              /*  top, right  */
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>

And an even better way might be to give the same margin to all item's sides, though half the value.

* {
  margin: 0;
  padding: 0;
}

html, body {
  box-sizing: border-box;
}

.container {
  width: 600px;
  margin: 0 auto;
  margin-top: 25px;
  border: 1px solid;
  padding: 5px;
}

.tags {
  list-style-type: none;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
}

.tag {
  padding: 5px;
  background-color: #76FF03;
  margin: 2.5px;
}
<div class="container">
  <ul class="tags">
    <li class="tag item-1">Tag Item 1</li>
    <li class="tag item-2">Tag Item 2</li>
    <li class="tag item-3">Tag Item 3</li>
    <li class="tag item-4">Tag Item 4</li>
    <li class="tag item-5">Tag Item 5</li>
    <li class="tag item-6">Tag Item 6</li>
    <li class="tag item-7">Tag Item 7</li>
    <li class="tag item-8">Tag Item 8</li>
    <li class="tag item-9">Tag Item 9</li>
    <li class="tag item-10">Tag Item 10</li>
    <li class="tag item-11">Tag Item 11</li>
    <li class="tag item-12">Tag Item 12</li>
    <li class="tag item-13">Tag Item 13</li>
    <li class="tag item-14">Tag Item 14</li>
    <li class="tag item-15">Tag Item 15</li>
    <li class="tag item-16">Tag Item 16</li>
  </ul>
</div>
Wednesday, June 2, 2021
 
subroutines
answered 6 Months ago
12

In general, for overflow: scroll (or auto and hidden) to work, a height constraint is needed in one way or the other, or else element's normally grow as much as needed to fit their content.

There is mainly 3 ways, where either an actual height is set, as in this first sample, where I added it to the container.

Stack snippet 1

.container {
  display: flex;
  height: 100vh;
}

.child {
  border: 1px solid grey;
  background-color: lightgrey;
  flex: 1 1 auto;
}

.controls-panel {
  display: flex;
  flex-direction: column;
}

.controls {
  flex: 0 0 auto;
}

.content-wrapper {
  flex: 1 1 auto;
  width: 400px;
  overflow-y: auto;
}
.content-item {
  display: inline-block;
  width: 100px;
  height: 100px;
  background-color: red;
}
<div class="container">
  <div class="child">
    <p>In real life I am an inline img.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I want my sibling to equal my height.</p>
  </div>
  <div class="child controls-panel">
    <div class="controls">
      <p>Small controls area. Panel below should scroll vertically.</p>
    </div>
    <div class="content-wrapper">
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
      <div class="content-item"></div>
    </div>
  </div>
</div>

Or to use absolute positioning to create that height constraint, which an absolute element does.

It is simply accomplished with an extra wrapper, content-scroll, and will keep the rest of the structure fully dynamic.

Stack snippet 2

.container {
  display: flex;
}

.child {
  border: 1px solid grey;
  background-color: lightgrey;
  flex: 1 1 auto;
}

.controls-panel {
  display: flex;
  flex-direction: column;
}

.controls {
  flex: 0 0 auto;
}

.content-wrapper {
  position: relative;
  flex: 1 1 auto;
  width: 400px;
}
.content-scroll {
  position: absolute;
  top: 0; left: 0;
  right: 0; bottom: 0;
  overflow-y: auto;
}
.content-item {
  display: inline-block;
  width: 100px;
  height: 100px;
  background-color: red;
}
<div class="container">
  <div class="child">
    <p>In real life I am an inline img.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I want my sibling to equal my height.</p>
  </div>
  <div class="child controls-panel">
    <div class="controls">
      <p>Small controls area. Panel below should scroll vertically.</p>
    </div>
    <div class="content-wrapper">
      <div class="content-scroll">
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
      </div>
    </div>
  </div>
</div>

Use Flexbox alone, thought it might not work fully cross browsers, especially with older versions, that still support it but is a lot buggier.

The simple fix in your original code is to change the flex: 1 1 auto; in .content-wrapper to flex: 1 1 0px; (0px were needed for my IE version, for Chrome/Firefox/Edge one can use 0)

Stack snippet 3

.container {
  display: flex;
}

.child {
  border: 1px solid grey;
  background-color: lightgrey;
  flex: 1 1 auto;
}

.controls-panel {
  display: flex;
  flex-direction: column;
}

.controls {
  flex: 0 0 auto;
}

.content-wrapper {
  flex: 1 1 0px;
  width: 400px;
  overflow-y: auto;
}
.content-item {
  display: inline-block;
  width: 100px;
  height: 100px;
  background-color: red;
}
<div class="container">
  <div class="child">
    <p>In real life I am an inline img.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I am some content whoop de doo.</p>
    <p>I want my sibling to equal my height.</p>
  </div>
  <div class="child controls-panel">
    <div class="controls">
      <p>Small controls area. Panel below should scroll vertically.</p>
    </div>
    <div class="content-wrapper">
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
    </div>
  </div>
</div>

Snippet 2 and 3 also answers your question

...with a vertically scrolling list, I cannot see how to make the height of its parent, the second .child, match the height of the first .child.

Note, if the top element in the right column can grow bigger than the left column, a minimum height will be needed on the bottom element, to prevent it from collapse into 0, e.g.

.content-wrapper {
  ...
  min-height: 200px;           /*  e.g. like this  */
}
Sunday, July 4, 2021
 
DaveRandom
answered 5 Months ago
65

When you create a flex container, an initial setting is align-content: stretch.

This causes multiple lines of flex items to distribute themselves evenly along the cross axis of the container. It's kind of like setting flex: 1 along the main axis: the flex items spread evenly across the line.

As a result, align-content: stretch may cause gaps when flex items wrap.

The simple solution is to override this setting with align-content: flex-start.

revised fiddle

html,
body {
  width: 100%;
  height: 100%;
}
#container {
  display: flex;
  height: 100%;
  background-color: blue;
}
.block {
  flex: 1;
}
#left {
  background-color: green;
}
#center {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-content: flex-start; /* NEW */
}
#right {
  background-color: orange;
}
.flexContainer {
  flex: 1;
  width: 50%;
  min-width: 100px;
  max-width: 50%;
  height: 150px;
  background-color: red;
  padding: 10px;
}
.flexDiv {
  width: 100%;
  height: 100%;
  background-color: yellow;
}
<div id="container">
  <div id="left" class="block">Left</div>
  <div id="center" class="block">
    <div class="flexContainer">
      <div class="flexDiv"></div>
    </div>
    <div class="flexContainer">
      <div class="flexDiv"></div>
    </div>
  </div>
  <div id="right" class="block">Right</div>
</div>

Reference:

8.4. Packing Flex Lines: the align-content property

The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.

The property accepts six values. stretch is the default.

stretch

Lines stretch to take up the remaining space. If the leftover free-space is negative, this value is identical to flex-start. Otherwise, the free-space is split equally between all of the lines, increasing their cross size.

The remaining values are: flex-start / flex-end / center / space-between / space-around

Wednesday, July 7, 2021
 
EastSw
answered 5 Months ago
60

You probably need to make sure the body is 100% high:

html, body {
    margin:0;
    height:100%;
    min-height:100%;
}

I made a fiddle.

Sunday, September 5, 2021
 
waylaidwanderer
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share