Is flexbox snapping to grid with text wrapping possible?

Viewed 286

So I like this, where it wraps but yet the boxes all align on both sides and fill the whole space.

<head>
<style>
* {
  padding: 0px;
  margin: 0px;
  text-transform: none;
  font-style: normal;
}

p {
  display: flex;
  flex-wrap: wrap;
  width: 200px;
}

i {
  flex: 1;
  padding: 5px;
  margin: 2px;
  background: #ddd;
  text-align: center;
}
</style>
</head>
<body>
  <p>
    <i>foo</i><i>hello</i><i>congratulations</i><i>forward</i><i>interesting</i><i>place</i><i>walk</i><i>to</i><i>anyplace</i><i>next</i><i>sophisticationism</i>
  </p>
</body>

Using a mix of small and large words, it somehow figures out how to optimally lay them out so it fills the space completely.

What I would like to do now is, instead of having each box be a dynamically width'd rectangle, I would like for the boxes to "snap to a grid" so to speak. That is, imagine there was a grid of squares that stretched across each row. Sort of like this (which I've completely hardcoded, just for the sake of demonstrating what it looks like. In reality this is what my question is about, how to make this automatic using FlexBox).

<head>
<style>
* {
  padding: 0px;
  margin: 0px;
  text-transform: none;
  font-style: normal;
}

p {
  display: flex;
  flex-wrap: wrap;
  width: 220px;
}

i {
  width: 50px;
  height: 50px;
  padding: 5px;
  margin: 2px;
  background: #ddd;
  text-align: center;
}

.l {
  width: 114px;
}
</style>
</head>
<body>
  <p>
    <i>a</i><i>a</i><i>a</i><i>a</i><i class='l'>long</i><i>a</i><i class='l'>long</i><i>a</i><i class='l'>a</i><i class='l'>long</i><i>a</i><i>a</i><i>a</i><i>a</i><i>a</i><i>a</i><i>a</i>
  </p>
</body>

So to rephrase, my question is how to cause flowing text (like the words in the images above) layout so (a) it fills each row, and (b) each box is a multiple of a square. That is, it snaps to the grid of a square, weather it's 1, 2, 3+ squares, rather than being 2.5 squares or 1.2345 squares or something. It always snaps to a whole block. It does this by first looking at the longer words, and calculating how many blocks it will take up. Then it stretches any shorter blocks (like the one letter "a" in the example above) so as to fill the blank space.

Wondering how this could be done with FlexBox or otherwise with CSS.

1 Answers

Try (I use split 1-3 words depends on length from here )

function show(n) {
  text.innerHTML = '<p>' + split(text.innerText,n).map(line=> {
    if(line.length==2 && line[0].length<n && line[1].length<n) {
      // case for two short words
      return `<i class='long'>${line[0]}</i><i class='short'>${line[1]}</i>`
    } else {
      return line.map(w=>`<i class='${w.length<9 ?'short':'long'}'>${w}</i>`).join('')
    }
  }).join('') + '</p>';
}


function split(str,n) {
  let z=0, b=[], r=[], w=str.split(' ');
  let s = w.map(v => v.length < n ? 1 : 2);

  s.map((v,i)=>( 
    z+v>=4 ? (r.push(b),z=0,b=[]) : 0,
    z+=v, b.push(w[i]) 
  ))

  return b.length ? r.concat([b]) : r;
}


show(9) // 9 is min number of letters for long words;
.short {
  flex-basis: 64px;
  flex-grow: 1
}

.long {
  flex-basis: 140px;
  flex-grow: 2
}

* {
  padding: 0px;
  margin: 0px;
  text-transform: none;
  font-style: normal;
}

p {
  display: flex;
  flex-wrap: wrap;
  width: 260px;
}

i {
  flex: 1;
  padding: 5px;
  margin: 2px;
  background: #ddd;
  text-align: center;
}
<div id="text">a aaa foo hello congratulations forward interesting place walk to anyplace next sophisticationism aa bb cccccccccc ddd</div>

Related