Search code examples
cssalignmentflexboxmultilineresponsive

Group of items with responsive equal width in multi-line context, issue with last-line


After researching the question for a while and considering all the float and text-align alternatives, I'm mostly happy with the solution that I've found using flexbox but there is an issue with the last line (when it overflows) in that the items are not the same width as those in the previous lines because they are allowed to stretch more (up to max-width) in order to try to fill the remaining empty space.

My requirements are simple:
1. All items must have the same width (including on the last line);
2. All items must be equallys spaced if the line is full or aligned left if not (typically last line);
3. Items can (must) overflow when min-width is reached (responsive resizing);
4. CSS-only solution and possibly avoiding media queries;
5. I'd rather use flex-basis: 0 for true same width (last line issue standing);
6. Non-flexbox solution considered as long as meet other requirements;

I've removed vendor prefixes for clarity but I'm using them on the actual page.

CSS

nav {
  margin:           20px 0px;
}
nav #linkgroup {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;   /* aligns left when not full */
  align-items: center;
  align-content: space-around;
}
nav #linkgroup .link {
  min-width: 120px;
  width: 120px;   /* only used because of Safari issue below */
  max-width: 160px;
  height: 30px;
  margin: 5px;
  flex-grow: 1;
  flex-basis: auto;   /* 0 gives all exact same size but have to use auto and set 'width' for Safari issue https://bugs.webkit.org/show_bug.cgi?id=136041 */
  line-height: 30px;   /* set equal to height, for vertical align trick */
  text-align: center;
}
nav #linkgroup .link a.btn {
  display: block;
  vertical-align: middle;
  line-height: normal;
  border-radius: 2px;
  padding: 5px 10px 5px 10px;
  border: solid #1f628d 2px;
  text-decoration: none;
}

HTML

  <nav>
    <div id="linkgroup">
      <div class="link"><a class="btn" href="#">A</a></div>
      <div class="link"><a class="btn" href="#">AB</a></div>
      <div class="link"><a class="btn" href="#">ABC</a></div>
      <div class="link"><a class="btn" href="#">ABCD</a></div>
      <div class="link"><a class="btn" href="#">ABCDE</a></div>
      <div class="link"><a class="btn" href="#">ABCDEF</a></div>
      <div class="link"><a class="btn" href="#">ABCDEFG</a></div>
    </div>
  </nav>

nav {
  margin:           20px 0px;
  padding:          0px 0px 5px 0px;   /* to space when multi-line */
}
nav #linkgroup {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;   /* aligns left when not full */
  align-items: center;
  align-content: space-around;
}
nav #linkgroup .link {
  min-width: 120px;
  width: 120px;   /* only used because of Safari issue below */
  max-width: 160px;
  height: 30px;
  margin: 5px;
  flex-grow: 1;
  flex-basis: auto;   /* 0 gives all exact same size but have to use auto and set 'width' for Safari issue https://bugs.webkit.org/show_bug.cgi?id=136041 */
  line-height: 30px;   /* set equal to height, for vertical align trick */
  text-align: center;
}
nav #linkgroup .link a.btn {
  display: block;
  vertical-align: middle;
  line-height: normal;
  border-radius: 2px;
  padding: 5px 10px 5px 10px;
  border: solid #1f628d 2px;
  text-decoration: none;
}
<nav>
    <div id="linkgroup">
      <div class="link"><a class="btn" href="#">A</a></div>
      <div class="link"><a class="btn" href="#">AB</a></div>
      <div class="link"><a class="btn" href="#">ABC</a></div>
      <div class="link"><a class="btn" href="#">ABCD</a></div>
      <div class="link"><a class="btn" href="#">ABCDE</a></div>
      <div class="link"><a class="btn" href="#">ABCDEF</a></div>
      <div class="link"><a class="btn" href="#">ABCDEFG</a></div>
    </div>
  </nav>


Solution

  • Found a solution:
    Simply add as many "empty" items as you expect the maximum on a line to be (a few more don't hurt but have at least enough for a full line) and then add the two bits relating to div:empty from the CSS below.

    CSS

    nav {
      margin:           20px 0px;
    }
    nav #linkgroup {
      display: flex;
      flex-wrap: wrap;
      justify-content: flex-start;   /* aligns left when not full */
      align-items: center;
      align-content: space-around;
    }
    nav #linkgroup .link {
      min-width: 120px;
      width: 120px;   /* only used because of Safari issue below */
      max-width: 160px;
      height: 30px;
      margin: 5px;
      flex-grow: 1;
      flex-basis: auto;   /* 0 gives all exact same size but have to use auto and set 'width' for Safari issue https://bugs.webkit.org/show_bug.cgi?id=136041 */
      line-height: 30px;   /* set equal to height, for vertical align trick */
      text-align: center;
    }
    nav #linkgroup .link a.btn {
      display: block;
      vertical-align: middle;
      line-height: normal;
      border-radius: 2px;
      padding: 5px 10px 5px 10px;
      border: solid #1f628d 2px;
      text-decoration: none;
    }
    #linkgroup > div:empty {
        height: 0;   /* I think visibility: hidden; would do the same */
    }
    @media only screen
     and (min-width: 1030px) {
      #linkgroup > div:empty {
          display: none;
      }
    }
    

    HTML

      <nav>
        <div id="linkgroup">
          <div class="link"><a class="btn" href="#">A</a></div>
          <div class="link"><a class="btn" href="#">AB</a></div>
          <div class="link"><a class="btn" href="#">ABC</a></div>
          <div class="link"><a class="btn" href="#">ABCD</a></div>
          <div class="link"><a class="btn" href="#">ABCDE</a></div>
          <div class="link"><a class="btn" href="#">ABCDEF</a></div>
          <div class="link"><a class="btn" href="#">ABCDEFG</a></div>
          <div class="link"></div>
          <div class="link"></div>
          <div class="link"></div>
          <div class="link"></div>
          <div class="link"></div>
          <div class="link"></div>
        </div>
      </nav>