skip to Main Content

I have a container with multiple flex elements inside, separated by a gap. These elements wrap within the container, and have a percentage-based size dictating how many can appear in a single row (ie, 4 per row = 25%).

Now, because the percentage-based size doesnt take into account the gap size, we must use a formula to determine the size of the elements, taking into account the gap we need (taken from here):

calc((100% / var(--flex-items)) - (((var(--flex-items) - 1) / var(--flex-items)) * var(--flex-gap)));

This works fine and well, an is easily adaptable for different amount of flex objects per row. But the issue comes in when changing the value of the gap, while transitioning it.

While the appearance before and after the transition appear as they should, during the transition, the calculation is for some reason, not being made correctly, and so one more item wraps than should, until settling into how it should look after the appearance. This only happens when DECREASING the value of gap — it animates as expected when increasing the gap

What is happening the during the animation between the 2 values that is causing the width calculation / gap value to not be in sync?

Codepen

Here

$( "body" ).on( "click", function() {
  $(".flexbox").toggleClass("bigger");
} );
:root {
    --flex-gap: 1rem;
}

.flexbox {
    display: flex;
    flex-wrap: wrap;
    gap: var(--flex-gap);
    justify-content: center;
    transition: gap 0.5s;
}

.flexbox.bigger {
  --flex-gap: 3rem;
}

.flex-items {
    --flex-items: 4;
    aspect-ratio: 2 / 1;
    display: flex;
    background: red;
    flex: 1 1 calc((100% / var(--flex-items)) - (((var(--flex-items) - 1) / var(--flex-items)) * var(--flex-gap)));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<html>
  <body>
    <div class="flexbox">
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
      <div class="flex-items"></div>
    </div>
  </body>
</html>

2

Answers


  1. I suspect the problem lies with a sort of edge effect with the widths being calculated, there may be some point at which part CSS pixels don’t quite fit within the actual screen pixels (tho’ I’m not certain).

    However, switching to grid overcomes the problem.

    $("body").on("click", function() {
      $(".flexbox").toggleClass("bigger");
    });
    :root {
      --flex-gap: 1rem;
    }
    
    .flexbox {
      display: flex;
      flex-wrap: wrap;
      gap: var(--flex-gap);
      justify-content: center;
      transition: gap 0.5s;
      display: grid;
      grid-template-columns: repeat(var(--items), 1fr);
      --items: 4;
    }
    
    .flexbox.bigger {
      --flex-gap: 3rem;
    }
    
    .flex-items {
      aspect-ratio: 2 / 1;
      background: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <html>
    
    <body>
      <div class="flexbox">
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
        <div class="flex-items"></div>
      </div>
    </body>
    
    </html>
    Login or Signup to reply.
  2. You need to apply a transition to the flex-basis on the flex items as well.

    $( "body" ).on( "click", function() {
      $(".flexbox").toggleClass("bigger");
    } );
    :root {
        --flex-gap: 1rem;
    }
    
    .flexbox {
        display: flex;
        flex-wrap: wrap;
        gap: var(--flex-gap);
        justify-content: center;
        transition: gap 0.5s;
    }
    
    .flexbox.bigger {
      --flex-gap: 3rem;
    }
    
    .flex-items {
        --flex-items: 4;
        aspect-ratio: 2 / 1;
        display: flex;
        background: red;
        flex: 1 1 calc((100% / var(--flex-items)) - (((var(--flex-items) - 1) / var(--flex-items)) * var(--flex-gap)));
        transition: flex-basis 0.5s;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <html>
      <body>
        <div class="flexbox">
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
          <div class="flex-items"></div>
        </div>
      </body>
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search