376

Is there a way to make a line break in multiple line flexbox?

For example to break after each 3rd item in this CodePen.

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
.item:nth-child(3n) {
  background: silver;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

Like

.item:nth-child(3n){
  /* line-break: after; */    
}
Artem Svirskyi
  • 7,305
  • 7
  • 31
  • 43
  • 1
    I had the same or very similar problem; I wanted to break every 4th item so I just set the width of each flex item to 25vw (or 25%). So in your case, for every 3rd item you would use 33.3vw (or 33.3%). Worked perfectly for what I wanted. Might help someone else if they are looking for a simpler method. – Ben Clarke May 14 '17 at 02:10
  • Related: https://stackoverflow.com/q/4609279/405017 – Phrogz Dec 03 '17 at 21:24

9 Answers9

431

The simplest and most reliable solution is inserting flex items at the right places. If they are wide enough (width: 100%), they will force a line break.

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}
.item:nth-child(4n - 1) {
  background: silver;
}
.line-break {
  width: 100%;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="line-break"></div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="line-break"></div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="line-break"></div>
  <div class="item">10</div>
</div>

But that's ugly and not semantic. Instead, we could generate pseudo-elements inside the flex container, and use order to move them to the right places.

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}
.item:nth-child(3n) {
  background: silver;
}
.container::before, .container::after {
  content: '';
  width: 100%;
  order: 1;
}
.item:nth-child(n + 4) {
  order: 1;
}
.item:nth-child(n + 7) {
  order: 2;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
</div>

But there is a limitation: the flex container can only have a ::before and a ::after pseudo-element. That means you can only force 2 line breaks.

To solve that, you can generate the pseudo-elements inside the flex items instead of in the flex container. This way you won't be limited to 2. But those pseudo-elements won't be flex items, so they won't be able to force line breaks.

But luckily, CSS Display L3 has introduced display: contents (currently only supported by Firefox 37):

The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced with its children and pseudo-elements in the document tree.

So you can apply display: contents to the children of the flex container, and wrap the contents of each one inside an additional wrapper. Then, the flex items will be those additional wrappers and the pseudo-elements of the children.

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  display: contents;
}
.item > div {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
.item:nth-child(3n) > div {
  background: silver;
}
.item:nth-child(3n)::after {
  content: '';
  width: 100%;
}
<div class="container">
  <div class="item"><div>1</div></div>
  <div class="item"><div>2</div></div>
  <div class="item"><div>3</div></div>
  <div class="item"><div>4</div></div>
  <div class="item"><div>5</div></div>
  <div class="item"><div>6</div></div>
  <div class="item"><div>7</div></div>
  <div class="item"><div>8</div></div>
  <div class="item"><div>9</div></div>
  <div class="item"><div>10</div></div>
</div>

Alternatively, according to an old version of the spec, Flexbox allowed forced breaks by using break-before, break-after or their old CSS 2.1 aliases:

.item:nth-child(3n) {
  page-break-after: always; /* CSS 2.1 syntax */
  break-after: always; /* CSS 3 syntax */
}

But these forced line breaks only work on Firefox, and I don't think they are supposed to work according to the current spec. The new proposed way (not implemented anywhere) is with wrap-before or wrap-after:

.item:nth-child(3n) {
  wrap-after: flex; /* New proposed syntax */
}

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}
.item:nth-child(3n) {
  page-break-after: always;
  break-after: always;
  wrap-after: flex;
  background: silver;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • @Oriol About the first approach, why you say it is ugly and not semantic? Just curious. – nacho4d Aug 26 '16 at 01:31
  • 30
    @nacho4d Because HTML should not be modified for styling purposes. And if you change your mind and decide you want 4 columns instead of 3, you would need to modify maybe lots of HTML. Compare with the `break-after` solution, which would only require modifying a selector in the stylesheet. – Oriol Aug 26 '16 at 01:36
  • 1
    I needed to add `display: block;` to the `.container` `::before` and `::after` pseudo classes to make solution number two work in IE. YMMV! – twined Dec 18 '16 at 22:01
  • 1
    @twined That's strange, because flex items should be blockified automatically. – Oriol Dec 18 '16 at 22:05
  • 3
    Since the page-break thing was apparently removed from the spec, is it possible to get your second snippet running in the column direction and not have it expand the height of its container? I didn't have any luck and setting flex-basis to 100% / items stretches its height. – Lucent Jun 16 '17 at 17:17
  • page-break-after is not working in chrome only works in firefox. – vee Jul 10 '17 at 06:09
  • 1
    Your second suggestion doesn't seem to work in IE10. IE11 and all of the other browsers I've tested are fine, but IE10 ignores the order of the pseudo elements. – Alex Podworny Aug 14 '17 at 21:50
  • 1
    The only elegant solution (the last one) works in Firefox. In any other browser you need to fool around with pseudo-elements. – Quolonel Questions Dec 11 '17 at 14:43
  • @VadimOvchinnikov But it's got a severe bug: https://hiddedevries.nl/en/blog/2018-04-21-more-accessible-markup-with-display-contents – TylerH Sep 13 '19 at 15:12
  • I had learning experience from this answer! Unfortunately only 1st solution works in IE11 :( If someone can't understand second example, it's because for [`order`](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Ordering_Flex_Items) property "If more than one item has the same integer value, then within that group the items are laid out as per source order." And you can consider "::before" element as " first child of the selected element." and `::after` element as "the last child of the selected element." I must say this was clever css (ab)use by author ;) – Mariusz Pawelski Aug 25 '20 at 16:57
  • 1
    Add `gap:20px` to your `.container` and get another headache: extra elements produce unnecessary gap. – Artyom Shegeda Oct 21 '22 at 23:41
  • 1
    This roundabout CSS hack is 100x "uglier and non-semantic" than using width: 100%. – Glenn Maynard Jan 04 '23 at 21:52
124

From my perspective it is more semantic to use <hr> elements as line breaks between flex items.

.container {
  display: flex;
  flex-flow: wrap;
}

.container hr {
  width: 100%;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <hr>
  <div>3</div>
  <div>2</div>
  ...
</div>

Tested in Chrome 66, Firefox 60 and Safari 11.

Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Petr Stepanov
  • 1,357
  • 1
  • 8
  • 4
  • 20
    This is how I do it too, works great. Adding hr { flex-basis: 100%; height: 0; margin: 0; border: 0; } makes the break seamless. – Besworks Mar 25 '19 at 22:00
  • 6
    I like this approach. Note: when using `gap: 10px;` the distance between rows is actually `20px`. To address, specify a row gap of half that size: `gap: 5px 10px;`. – CuddleBunny Aug 05 '19 at 16:24
  • 1
    @Besworks: `border` should be set to `none`, instead of `0` – Mark Oct 28 '19 at 11:33
  • @mark, `border:0;` is just as valid as `border:none;`. See: https://stackoverflow.com/questions/2922909/should-i-use-border-none-or-border-0 – Besworks Nov 15 '19 at 20:46
  • Yes, this worked for my specific use case – alextrastero Jun 02 '22 at 14:20
36

@Oriol has an excellent answer, sadly as of October 2017, neither display:contents, neither page-break-after is widely supported, better said it's about Firefox which supports this but not the other players, I have come up with the following "hack" which I consider better than hard coding in a break after every 3rd element, because that will make it very difficult to make the page mobile friendly.

As said it's a hack and the drawback is that you need to add quite a lot of extra elements for nothing, but it does the trick and works cross browser even on the dated IE11.

The "hack" is to simply add an additional element after each div, which is set to display:none and then used the css nth-child to decide which one of this should be actually made visible forcing a line brake like this:

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}
.item:nth-child(3n-1) {
  background: silver;
}
.breaker {
  display: none;
}
.breaker:nth-child(3n) {
  display: block;
  width: 100%;
  height: 0;
}
<div class="container">
  <div class="item">1</div>
  <p class="breaker"></p>
  
  <div class="item">2</div>
  <p class="breaker"></p>
  
  <div class="item">3</div>
  <p class="breaker"></p>
  
  <div class="item">4</div>
  <p class="breaker"></p>
  
  <div class="item">5</div>
  <p class="breaker"></p>
  
  <div class="item">6</div>
  <p class="breaker"></p>
  
  <div class="item">7</div>
  <p class="breaker"></p>
  
  <div class="item">8</div>
  <p class="breaker"></p>
  
  <div class="item">9</div>
  <p class="breaker"></p>
  
  <div class="item">10</div>
  <p class="breaker"></p>
</div>
henzel
  • 11
  • 4
Emil Borconi
  • 3,326
  • 2
  • 24
  • 40
  • 2
    I also found that the "display:contents" and "page-break-after" methods are not working, and resorted to this "hack." This was reported as a Chrome bug, and marked as "WontFix" (see https://bugs.chromium.org/p/chromium/issues/detail?id=473481) with the explanation: "There is, according to the CSS Working Group, no current way to force a line break in a flex box with CSS." – Martin_W Jun 04 '18 at 23:27
  • You could save a touch of clutter by using the selector `.container>p`. Then all those `

    ` tags wouldn't need the `class` attribute. Not _important_ of course. Just my lazy brain finding a tiny, space-saving tweak to your clever solution. Of course, it also relies on the user having no other `

    ` tags as direct children of the `.container` div. Technically you could do the same with all the other `

    ` children, but you're a lot more likely to have other `
    `s in the `.container` than you are `

    `s, so probably not a smart move there.

    – Steve Aug 22 '18 at 06:52
19

You want a semantic linebreak?

Then consider using <br>. W3Schools may suggest you that BR is just for writing poems (mine is coming soon) but you can change the style so it behaves as a 100% width block element that will push your content to the next line. If 'br' suggests a break then it seems more appropriate to me than using hr or a 100% div and makes the html more readable.

Insert the <br> where you need linebreaks and style it like this.

 // Use `>` to avoid styling `<br>` inside your boxes 
 .container > br 
 {
    width: 100%;
    content: '';
 }

You can disable <br> with media queries, by setting display: to block or none as appropriate (I've included an example of this but left it commented out).

You can use order: to set the order if needed too.

And you can put as many as you want, with different classes or names :-)

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}

.container > br
{
  width: 100%;
  content: '';
}

// .linebreak1 
// { 
//    display: none;
// }

// @media (min-width: 768px) 
// {
//    .linebreak1
//    {
//       display: block;
//    }
// }
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <br class="linebreak1"/>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

No need to limit yourself to what W3Schools says:

enter image description here

Fer To
  • 1,487
  • 13
  • 24
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • One extension of technique is to put `
    ` after every second item, `
    ` after every third. Then apply a class `cols-2` to the container and create css to only enable the appropriate linebreaks for that number of columns. eg. `br { display: none; } .cols-2 br.2col { display: block; }`
    – Simon_Weaver Jun 21 '18 at 22:33
  • 1
    No, a `br` is not for line breaking _elements_, it is for _text_: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br ... https://stackoverflow.com/questions/3937515/when-to-use-br-line-breaks-vs-css-positioning – Asons Jun 22 '18 at 05:47
  • 3
    I’ll change my wording so as not to present this as a perfect solution but for some cases I don’t see this as any worse than other div or pseudo element solutions. Maybe I’ll go write a poem about it now. – Simon_Weaver Jun 22 '18 at 06:16
  • Yeah...a poem would be nice, don't forget to post a link to it here :) ... Regarding a perfect solution, there is one (`break-*` shown in the accepted answer) though unfortunately it has not reach cross browsers yet, so the second best is to use an element that natively fill its parent's width and push any next siblings to a row of themselves, which again is given in the accepted answer. So by using any other element than a block like one would be worse, semantically, like the `br`. – Asons Jun 22 '18 at 06:31
  • Note, single line comments `//` is invalid in CSS and can cause an error in a browser resulting in it abort parsing the rules, instead use multi line `/* */` – Asons Jun 22 '18 at 09:02
  • Works good. But my container goes below 1-2 px more than the normal solution. i.e. `flex-flow;wrap` with `width:100%` – vusan Aug 21 '18 at 11:11
  • 8
    Remember, you post a print of W3Schools, not W3C, they aren't connected. – Edu Ruiz Dec 17 '18 at 19:30
  • @Simon_Weaver Quoting w3schools will harm your score – neoswf May 06 '22 at 22:35
9

I think the traditional way is flexible and fairly easy to understand:

Markup

<div class="flex-grid">
    <div class="col-4">.col-4</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-4">.col-4</div>
    <div class="col-4">.col-4</div>

    <div class="col-3">.col-3</div>
    <div class="col-9">.col-9</div>

    <div class="col-6">.col-6</div>
    <div class="col-6">.col-6</div>
</div>

Create grid.css file:

.flex-grid {
  display: flex;
  flex-flow: wrap;
}

.col-1 {flex: 0 0 8.3333%}
.col-2 {flex: 0 0 16.6666%}
.col-3 {flex: 0 0 25%}
.col-4 {flex: 0 0 33.3333%}
.col-5 {flex: 0 0 41.6666%}
.col-6 {flex: 0 0 50%}
.col-7 {flex: 0 0 58.3333%}
.col-8 {flex: 0 0 66.6666%}
.col-9 {flex: 0 0 75%}
.col-10 {flex: 0 0 83.3333%}
.col-11 {flex: 0 0 91.6666%}
.col-12 {flex: 0 0 100%}

[class*="col-"] {
  margin: 0 0 10px 0;

  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

@media (max-width: 400px) {
  .flex-grid {
    display: block;
  }
}

I've created an example (jsfiddle)

Try to resize the window under 400px, it's responsive!!

Moshe Quantz
  • 1,480
  • 2
  • 12
  • 8
8

I just want to throw this answer in the mix, intended as a reminder that – given the right conditions – you sometimes don't need to overthink the issue at hand. What you want might be achievable with flex: wrap and max-width instead of :nth-child.

ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 420px;
  list-style-type: none;
  background-color: tomato;
  margin: 0 auto;
  padding: 0;
}

li {
  display: inline-block;
  background-color: #ccc;
  border: 1px solid #333;
  width: 23px;
  height: 23px;
  text-align: center;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0.2rem;
  flex-shrink: 0;
}
<div class="root">
  <ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
    <li>E</li>
    <li>F</li>
    <li>G</li>
    <li>H</li>
    <li>I</li>
    <li>J</li>
    <li>K</li>
    <li>L</li>
    <li>M</li>
    <li>N</li>
    <li>O</li>
    <li>P</li>
    <li>Q</li>
    <li>R</li>
    <li>S</li>
    <li>T</li>
    <li>U</li>
    <li>V</li>
    <li>W</li>
    <li>X</li>
    <li>Y</li>
    <li>Z</li>
  </ul>
</div>

https://jsfiddle.net/age3qp4d/

TylerH
  • 20,799
  • 66
  • 75
  • 101
WoodrowShigeru
  • 1,418
  • 1
  • 18
  • 25
4

Another possible solution that doesn't require to add any extra markup is to add some dynamic margin to separate the elements.

In the case of the example, this can be done with the help of calc(), just adding margin-left and margin-right to the 3n+2 element (2, 5, 8)

.item:nth-child(3n+2) {
  background: silver;
  margin: 10px calc(50% - 175px);
}

Snippet Example

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
.item:nth-child(3n+2) {
  background: silver;
  margin: 10px calc(50% - 175px);
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>
Juanma Menendez
  • 17,253
  • 7
  • 59
  • 56
  • 1
    This deserves a vote. Using combination of flex and margin is a really simple way to support line breaks. It also works really well with `calc` as outlined in this answer. – stwilz Jul 29 '19 at 01:20
  • I like this better, just `margin-right: 1px` the item, and it will make the next item start at a new row. – arvil Sep 04 '19 at 00:06
  • @arvil could you post a solution? – Alex Dec 08 '21 at 21:35
-3

For future questions, It's also possible to do it by using float property and clearing it in each 3 elements.

Here's an example I've made.

.grid {
  display: inline-block;
}

.cell {
  display: inline-block;
  position: relative;
  float: left;
  margin: 8px;
  width: 48px;
  height: 48px;
  background-color: #bdbdbd;
  font-family: 'Helvetica', 'Arial', sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  text-indent: 4px;
  color: #fff;
}

.cell:nth-child(3n) + .cell {
  clear: both;
}
<div class="grid">
  <div class="cell">1</div>
  <div class="cell">2</div>
  <div class="cell">3</div>
  <div class="cell">4</div>
  <div class="cell">5</div>
  <div class="cell">6</div>
  <div class="cell">7</div>
  <div class="cell">8</div>
  <div class="cell">9</div>
  <div class="cell">10</div>
</div>
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Gabriel
  • 59
  • 1
-6

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}

.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
<div class="container">
  <div>
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
  </div>
  <div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
  </div>
  <div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
  </div>
  <div class="item">10</div>
</div>

you could try wrapping the items in a dom element like here. with this you dont have to know a lot of css just having a good structure will solve the problem.

Naseeruddin V N
  • 597
  • 5
  • 17
  • 1
    You could make container a normal `display: block` and make those new tier 2 divs flexboxes. This works for rows. Replace the divs with spans when using column mode. – jiggunjer Sep 02 '17 at 06:38