Make CSS grid or flex item equal size with stretched separator between

Viewed 254

I try to build a dynamic toolbar where:

  • The number of tools is dynamic
  • All tools should have the same width (based on the widest one)
  • Tools can be separated by a separator that takes all the space available (stretched)
  • The separator can be placed anywhere
  • The html can't be changed

Expected output (given BBB the widest tool):

—————————————————————————————————————————————————————
| A |BBB| CC|            SEPARATOR              | D |
—————————————————————————————————————————————————————

Flex

I tried with the flex method, I can't combine all the rules:

  • Either the separator takes all the space but tools width are not equal:
—————————————————————————————————————————————————————
|A|BBB|CC|       SEPARATOR                        |D|
—————————————————————————————————————————————————————

nav {
  display: flex;
  background: #e8e8e8;
  width: 100%;
}

.item {
  flex: 1;
  text-align: center;
}

.separator {
  width: 100%;
  background: #d3d3d3;
}
<nav>
  <div class="item">A</div>
  <div class="item">BBB</div>
  <div class="item">CC</div>
  <div class="separator"></div>
  <div class="item">D</div>
</nav>

  • Either all tools (including the separator) have the same width:
—————————————————————————————————————————————————————
|    A    |   BBB   |    CC   | SEPARATOR |    D    |
—————————————————————————————————————————————————————

nav {
  display: flex;
  background: #e8e8e8;
  width: 100%;
}

.item {
  flex: 1;
  text-align: center;
}

.separator {
  flex: 1;
  background: #d3d3d3;
}
<nav>
  <div class="item">A</div>
  <div class="item">BBB</div>
  <div class="item">CC</div>
  <div class="separator"></div>
  <div class="item">D</div>
</nav>

Grid

With the grid system, I can't get the separator without specifying a grid-template-columns, which I want to avoid. I need something dynamic.

nav {
  display: grid;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  background: #e8e8e8;
  width: 100%;
}

.item {
  text-align: center;
}

.separator {
  justify-self: stretch;
  background: #d3d3d3;
}
<nav>
  <div class="item">A</div>
  <div class="item">BBB</div>
  <div class="item">CC</div>
  <div class="separator"></div>
  <div class="item">D</div>
</nav>

I'm open to JavaScript solutions if there is no CSS solution. Thank you for your help.

1 Answers

via javascript , you can loop through .item and look for the widest , then update a custom css property.

possible example via js and flex

var bigW = "0";

for (let e of document.querySelectorAll("nav .item")) {
  elW = e.offsetWidth;
  if (elW > bigW) {
    bigW = elW;
  }
  document.documentElement.style.setProperty("--myW", bigW + 1 + "px");
}
nav {
  display: flex;
  background: #e8e8e8;
  width: 100%;
}

.item {
  min-width: var(--myW, 3em);
  text-align: center;
}

.separator {
  flex: 1;
  background: #d3d3d3;
}

nav div+div {
  border-left: solid;
}
<nav>
  <div class="item">A</div>
  <div class="item">BBB</div>
  <div class="item">CC</div>
  <div class="separator"></div>
  <div class="item">D</div>
</nav>


edit from a comment below

var bigW = "0";

for (let e of document.querySelectorAll("nav > div")) {
  elW = e.offsetWidth;
  if (elW < 7) {// includes partially possible border and padding, mind it
    e.style.flexGrow = 1;
  } else if (elW > bigW) {
    bigW = elW;
  }
}
document.documentElement.style.setProperty("--myW", bigW + 1 + "px");
nav {
  display: flex;
  background: #e8e8e8;
}

.item {
  min-width: var(--myW, 0);
  text-align: center;
  border: solid 1px;
}

.separator {
  background: #d3d3d3;
}
<nav>
  <div class="item">A</div>
  <div class="item">BBBBBBBB</div>
  <!--<div class="separator"></div>-->
  <div class="item">CC</div>
  <div class="separator"></div>
  <div class="item">D</div>
</nav>

Related