skip to Main Content

Like the title says, when flex-wrap:wrap is enabled on a parent div, the child grid containers double in height. I’d like it to not do that, but more importantly, why is this happening?

I assumed the height of the grid would simply adjust if/when more grid-items are added to cause it to wrap…but I’m clearly missing something.

I’d like it to look like it does when flex-wrap:nowrap is enabled, but also I want it to wrap….

View it on Codepen

.container {
  width: 88%;
  margin: 2rem auto;
  max-width: 1400px;
}

.cno-event-search-filters__container {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.cno-event-search-filters__filter-container {
  border: 2px solid black;
  display: grid;
  grid-template-columns: repeat(auto-fit, max(240px));
  gap: 1rem;
}
<div class="container">
  <div class="cno-event-search-filters">
    <div class="cno-event-search-filters__container">
      <h4 class="cno-event-search-filters__title">Event Types</h4>
      <div class="cno-event-search-filters__filter-container">
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Culture">
          <label for="Culture">Culture</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Entertainment">
          <label for="Entertainment">Entertainment</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Sports">
          <label for="Sports">Sports</label>
        </div>
      </div>
    </div>
    <div class="cno-event-search-filters__container">
      <h4 class="cno-event-search-filters__title">Locations</h4>
      <div class="cno-event-search-filters__filter-container">
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Ampitheatre">
          <label for="Ampitheatre">Ampitheatre</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Capitol Lawn">
          <label for="Capitol Lawn">Capitol Lawn</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Capitol Museum">
          <label for="Capitol Museum">Capitol Museum</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Chapel">
          <label for="Chapel">Chapel</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Choctaw Village">
          <label for="Choctaw Village">Choctaw Village</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Playground">
          <label for="Playground">Playground</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Red Warrior Park">
          <label for="Red Warrior Park">Red Warrior Park</label>
        </div>
        <div class="cno-event-search-filters__filter">
          <input type="checkbox" id="Stickball Field">
          <label for="Stickball Field">Stickball Field</label>
        </div>
      </div>
    </div>
  </div>
</div>

2

Answers


  1. I guess is a glitch of browser trying to automatically calculate sizes, give it some help deifining that your container is full width with a width: 100% in &__filter-container

    &__filter-container {
        border: 2px solid black;
        display: grid;
        grid-template-columns: repeat(auto-fit, max(240px));
        gap: 1rem;
        width: 100%; /** <----- here! */
    }
    

    Here is the codepen: https://codepen.io/Crist-bal-D-az-lvarez/pen/oNaydwO

    Login or Signup to reply.
  2. This quirk is a bit tricky so I will try to use simple words to explain what’s happening.

    First, when defining flex-wrap you control the nature of the layout like detailed in the Specification:

    The flex-wrap property controls whether the flex container is single-line or multi-line,

    nowrap

    The flex container is single-line.

    wrap

    The flex container is multi-line.

    Even if in your case, you have one line when you add flex-wrap: wrap your layout is still considered as "multi-line" and the algorithm is a bit different from the "single-line" one.

    The difference is related to how of the size of each line is calculated which also affect the size of the items. Here it’s about the width since we have a column direction.

    When you have a "single-line" layout (flex-wrap: nowrap), calculating the width of the line is easy. From the Specification

    If the flex container is single-line and has a definite cross size, the cross size of the flex line is the flex container’s inner cross size.

    Your container is a block element so its full width (the definite cross size). That width is used as the size of the line and the element inside will stretch to fit that size. A logical behavior BUT we can change it we remove that stretch part.

    .container {
      width: 88%;
      margin: 2rem auto;
      max-width: 1400px;
    }
    
    .cno-event-search-filters__container {
      display: flex;
      flex-direction: column;
      align-items: start;
    }
    
    .cno-event-search-filters__filter-container {
      border: 2px solid black;
      display: grid;
      grid-template-columns: repeat(auto-fit, max(240px));
      gap: 1rem;
    }
    <div class="container">
      <div class="cno-event-search-filters">
        <div class="cno-event-search-filters__container">
          <h4 class="cno-event-search-filters__title">Event Types</h4>
          <div class="cno-event-search-filters__filter-container">
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Culture">
              <label for="Culture">Culture</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Entertainment">
              <label for="Entertainment">Entertainment</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Sports">
              <label for="Sports">Sports</label>
            </div>
          </div>
        </div>
        <div class="cno-event-search-filters__container">
          <h4 class="cno-event-search-filters__title">Locations</h4>
          <div class="cno-event-search-filters__filter-container">
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Ampitheatre">
              <label for="Ampitheatre">Ampitheatre</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Capitol Lawn">
              <label for="Capitol Lawn">Capitol Lawn</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Capitol Museum">
              <label for="Capitol Museum">Capitol Museum</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Chapel">
              <label for="Chapel">Chapel</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Choctaw Village">
              <label for="Choctaw Village">Choctaw Village</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Playground">
              <label for="Playground">Playground</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Red Warrior Park">
              <label for="Red Warrior Park">Red Warrior Park</label>
            </div>
            <div class="cno-event-search-filters__filter">
              <input type="checkbox" id="Stickball Field">
              <label for="Stickball Field">Stickball Field</label>
            </div>
          </div>
        </div>
      </div>
    </div>

    Note how you only have one column of 240px inside your grid because your element no more stretch inside the whole line and the CSS grid algorithm will create only one column following its algorithm

    When auto-fill is given as the repetition number, if the grid container has a definite size or max size in the relevant axis, …. Otherwise, if the grid container has a definite min size in the relevant axis … Otherwise, the specified track list repeats only once.

    Your grid container has no definite size, no max size and no min size so we fall into the last "otherwise" and we end with one repetition. (note that auto-fit and auto-fill have the same algorithm here)

    Now, when you have a "multi-line" layout, the algorithm of defining the number of lines and the line sizes is more complex and different form the "single-line" layout even if at the end you have only one line. This is the trickiest part and what you need to know is that the size of the items is used to identify the size of the lines.

    In other words, we need to first identify the element size to find the size of the lines and your grid container has no definite size, no max size and no min size so we fall into the same previous case where we end with one column. Then the flexbox algorithm will identify one line. Then the stretch alignment will strike to make your grid container full width and the auto-fit algorithm will strike another time to create more column BUT the height of your grid container won’t change and here is the quirk.

    Before finding the number of lines and defining their size we first calculate the width/height of the grid container. After finding the number of lines and their sizes, the width will get updated (due to the stretch alignment) but the height won’t change. The number of columns will change because we have space for them.

    If you do a comparison, you will find that the height is equal to the size of the container without grid-template-columns. Add flex-wrap: wrap and use the Dev tool to disable the grid-template-columns and the height won’t change. That height is also the same when we disable the stretch alignment

    enter image description here

    Here is a simplified version of your case. Resize the main container and notice how the height of the inner one won’t change.

    .box {
      display: flex;
      flex-direction: column;
      flex-wrap: wrap;
      /* align-items: start; try to disable this to see the difference */
      border:2px solid #000;
      overflow: hidden;
      resize: horizontal;
    }
    .box > div {
      display: grid;
      gap: 1rem;
      grid-template-columns: repeat(auto-fill, 100px);
      border:2px solid red;
    }
    <div class="box">
      <div>
        <div>One</div>
        <div>Two</div>
        <div>Three</div>
      </div>
    </div>

    To fix this and allow the height to change you need to have either a "defined size, a max size or min size" like in the algorithm.

    a min-width: 100% or width: 100% can do the job

    .box {
      display: flex;
      flex-direction: column;
      flex-wrap: wrap;
      /* align-items: start; try to disable this to see the difference */
      border:2px solid #000;
      overflow: hidden;
      resize: horizontal;
    }
    .box > div {
      display: grid;
      width: 100%;
      gap: 1rem;
      grid-template-columns: repeat(auto-fill, 100px);
      border:2px solid red;
      box-sizing: border-box;
    }
    <div class="box">
      <div>
        <div>One</div>
        <div>Two</div>
        <div>Three</div>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search