Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexbox space-between has extra 1px gap at the end when using calc()

I was developing a grid system using flexbox's justify-content: space-between; and I noticed that on certain screen sizes (about every other pixel) a 1 pixel gap appears at the end of certain columns.

If you start to resize your browser with the example below, you can see the 1 pixel space appearing and disappearing.

Example

* {
  margin: 0;
  padding: 0;
}

.row {
  display: flex;
  flex-wrap: wrap;
  max-width: 550px;
  width: 100%;
  margin: 0 auto;
  border: 2px solid #888;
  justify-content: space-between;
}

div[class^="col"] {
  border: 2px solid red;
}

.col-12 {
  flex-basis: calc(100% - 4px);
}

.col-11 {
  flex-basis: calc(11/12*100% - (1.167 - 11/12)*24px);
}

.col-10 {
  flex-basis: calc(5/6*100% - (1.167 - 5/6)*24px);
}

.col-9 {
  flex-basis: calc(3/4*100% - (1.167 - 3/4)*24px);
}

.col-8 {
  flex-basis: calc(2/3*100% - (1.167 - 2/3)*24px);
}

.col-7 {
  flex-basis: calc(7/12*100% - (1.167 - 7/12)*24px);
}

.col-6 {
  flex-basis: calc(1/2*100% - (1.167 - 1/2)*24px);
}

.col-5 {
  flex-basis: calc(5/12*100% - (1.167 - 5/12)*24px);
}

.col-4 {
  flex-basis: calc(1/3*100% - (1.167 - 1/3)*24px);
}

.col-3 {
  flex-basis: calc(1/4*100% - (1.167 - 1/4)*24px);
}

.col-2 {
  flex-basis: calc(1/6*100% - (1.167 - 1/6)*24px);
}

.col-1 {
  flex-basis: calc(1/12*100% - (1.167 - 1/12)*24px);
}
<div class="row">
  <div class="col-12">col-12</div>
  <div class="col-6">col-6</div>
  <div class="col-6">col-6</div>
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-6">col-6</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-4">col-4</div>
  <div class="col-8">col-8</div>
  <div class="col-7">col-7</div>
  <div class="col-5">col-5</div>
  <div class="col-6">col-6</div>
  <div class="col-4">col-4</div>
  <div class="col-2">col-2</div>
</div>

Here is a working Fiddle so you can resize it more easily.

JSFiddle

*Note: I know that I'm doing some crazy stuff with the calc(), but even if you just subtract 5px instead of (1.167 - 1/12)*24px) the issue still exists.

Is this a bug with flexbox?

What do I need to do in order to remove the gap at the end of these columns?

like image 897
Hunter Turner Avatar asked Jun 19 '19 14:06

Hunter Turner


1 Answers

The Problem

I played around with this problem a little bit before coming to a solution. The steps as follows:

First I converted everything to use a standard format

calc(a/12 * 100% - (7/6 - a/12) * 24px) where a = "col number"

Next I began experimenting with the number to see the actual output without units (of course)

  • 1: -17.6666...
  • 2: -7.3333...
  • 3: 3
  • 4: 13.3333...
  • 5: 23.6666...
  • 6: 34
  • 7: 44.3333...
  • 8: 54.6666...
  • 9: 65
  • 10: 75.3333...
  • 11: 85.6666...
  • 12: 96

Form this we can see three things

  • Any number divisible by 3 is a whole number
  • The next number ends by repeating x.3333...
  • The second number ends in x.6666...

Note: 1 and 2 are special cases and are reversed because their output is actually negative. We can assume that their numbers would be positive in the real world and would fall inline with the others

After that pinpointing the actual issue in the equation was easy: a/12 * 100%

So the rounding issue is your problem - i.e. when you use two or more numbers ending in x.3333...

The Fix

We would like these rounding errors not to cascade so we have a few options

  1. Add an subtract the values to get some middle number
  2. Add some half pixel to decrease the error and hopefully round the issue away (only add half pixel to numbers ending in x.3333...)

Examples:

  1. flex-basis: calc((8/12 - 1/6/100) * 100% - (7/6 - 8/12) * 24px);
  2. flex-basis: calc(8/12 * 100% - (7/6 - 8/12) * 24px);

I went with the first example to give me this repeating code:

* {
  margin: 0;
  padding: 0;
}

.row {
  display: flex;
  flex-wrap: wrap;
  max-width: 550px;
  width: 100%;
  margin: 0 auto;
  border: 2px solid #888;
  justify-content: space-between;
}

div[class^="col"] {
  border: 2px solid red;
}

.col-12 {
    flex-basis: calc(12/12 * 100% - (7/6 - 12/12) * 24px);
}
.col-11 {
    flex-basis: calc((11/12 - 1/6/100) * 100% - (7/6 - 11/12) * 24px);
}
.col-10 {
    flex-basis: calc((10/12 + 1/6/100) * 100% - (7/6 - 10/12) * 24px);
}
.col-9 {
    flex-basis: calc(9/12 * 100% - (7/6 - 9/12) * 24px);
}
.col-8 {
    flex-basis: calc((8/12 - 1/6/100) * 100% - (7/6 - 8/12) * 24px);
}
.col-7 {
    flex-basis: calc((7/12 + 1/6/100) * 100% - (7/6 - 7/12) * 24px);
}
.col-6 {
    flex-basis: calc(6/12 * 100% - (7/6 - 6/12) * 24px);
}
.col-5 {
    flex-basis: calc((5/12 - 1/6/100) * 100% - (7/6 - 5/12) * 24px);
}
.col-4 {
    flex-basis: calc((4/12 + 1/6/100) * 100% - (7/6 - 4/12) * 24px);
}
.col-3 {
    flex-basis: calc(3/12 * 100% - (7/6 - 3/12) * 24px);
}
.col-2 {
    flex-basis: calc((2/12 - 1/6/100) * 100% - (7/6 - 2/12) * 24px);
}
.col-1 {
    flex-basis: calc((1/12 + 1/6/100) * 100% - (7/6 - 1/12) * 24px);
}
<div class="row">
  <div class="col-12">col-12</div>
  <div class="col-6">col-6</div>
  <div class="col-6">col-6</div>
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
  <div class="col-4">col-4</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-6">col-6</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-3">col-3</div>
  <div class="col-4">col-4</div>
  <div class="col-8">col-8</div>
  <div class="col-7">col-7</div>
  <div class="col-5">col-5</div>
  <div class="col-6">col-6</div>
  <div class="col-4">col-4</div>
  <div class="col-2">col-2</div>
</div>

This should work in modern and even most older browsers that support non-rounded calc

If you would like to see the second solution just leave a comment and I'll post it.

Note: if you can it would be best to use a language like sass or less to perform these calculations as they after rounding and also functions to keep all this hard work in one place.

like image 84
James Wasson Avatar answered Nov 05 '22 17:11

James Wasson