A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.md 13KB

4 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. title: Flexbox Grid Finesse
  2. url: http://www.heydonworks.com/article/flexbox-grid-finesse
  3. hash_url: 47b2c38919f07b29e902dc35979384ce
  4. <div>
  5. <p class="intro"><em>(This post was originally published on Medium, put has a permanent home here.)</em></p>
  6. <p>Flexbox—not to be confused with <a href="https://en.wikipedia.org/wiki/Sex_Box">Sex Box</a>, the British TV show wherein Mariella Frostrup interviews people who’ve just had sex in a box—is the CSS layout toolkit <em>de rigueur</em>. Of all the celebrated features of Flexbox, it is the light work it makes of producing wrappable grids, tolerant of dynamic content, that I think’s integral. </p>
  7. <p>In this article, I’ll cover a few techniques to exploit Flexbox’s internal algorithms and design finessed grids intended for changing quantities and dimensions of content.</p>
  8. <h2 id="basic-wrapping">Basic wrapping</h2>
  9. <pre class="prettyprint"><code class=" hljs css"><span class="hljs-class">.parent</span> <span class="hljs-rules">{&#13;
  10. <span class="hljs-rule"><span class="hljs-attribute">display</span>:<span class="hljs-value"> flex</span></span>;&#13;
  11. <span class="hljs-rule"><span class="hljs-attribute">flex-flow</span>:<span class="hljs-value"> row wrap</span></span>;&#13;
  12. <span class="hljs-rule">}</span></span>&#13;
  13. &#13;
  14. <span class="hljs-class">.child</span> <span class="hljs-rules">{&#13;
  15. <span class="hljs-rule"><span class="hljs-attribute">flex</span>:<span class="hljs-value"> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">25</span>%</span></span>;&#13;
  16. <span class="hljs-rule">}</span></span></code></pre>
  17. <p>I’ve made the <code>.parent</code> a flex container and used the <code>flex-flow</code> shorthand to set the <code>flex-direction</code> to <code>row</code> (i.e. following the horizontal axis) and enabled wrapping. Wrapping operates according to the <code>flex-basis</code> set on children; in this case <code>25%</code>. </p>
  18. <p>100 divided by 25 is 4, meaning what we have here is a 4 column grid. Simple stuff, I know. With wrapping on, a new row is begun every time you exceed 4 additional children. The last row is always complete, either because the total is exactly divisible by 4, or because the left over children are “grown” to share a row’s width. The “1” in <code>flex: 1 0 25%</code> essentially means the ability for children to grow is set to <em>on</em>.</p>
  19. <p>The upshot is that you can employ a grid which “tidies” itself, distributing children automatically. This is powerful stuff. I can be assured that no matter the number of children currently included, my layout will be acceptable.</p>
  20. <h2 id="element-queries">Element queries</h2>
  21. <p>I’m not the first (I think <a href="https://twitter.com/zomigi">Zoe Gillenwater</a> pointed it out) to notice that Flexbox can be made to employ something akin to “element queries”; changes in an element’s layout based on that element’s own dimensions. By incorporating a <code>min-width</code> into the last example, I can trigger elements to grow at this “breakpoint”. </p>
  22. <pre class="prettyprint"><code class=" hljs css"><span class="hljs-class">.parent</span> <span class="hljs-rules">{&#13;
  23. <span class="hljs-rule"><span class="hljs-attribute">display</span>:<span class="hljs-value"> flex</span></span>;&#13;
  24. <span class="hljs-rule"><span class="hljs-attribute">flex-flow</span>:<span class="hljs-value"> row wrap</span></span>;&#13;
  25. <span class="hljs-rule">}</span></span>&#13;
  26. &#13;
  27. <span class="hljs-class">.child</span> <span class="hljs-rules">{&#13;
  28. <span class="hljs-rule"><span class="hljs-attribute">flex</span>:<span class="hljs-value"> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">25</span>%</span></span>;&#13;
  29. <span class="hljs-rule"><span class="hljs-attribute">min-width</span>:<span class="hljs-value"> <span class="hljs-number">5</span>em</span></span>;&#13;
  30. <span class="hljs-rule">}</span></span></code></pre>
  31. <p>I have made my grid entirely responsive with just one additional declaration. The number of columns is predicated on the simple axiom that no one column (element) can be fewer than <code>5em</code> in width. Flexbox’s <code>grow</code> takes care of expanding children that hit this minimum width, making sure they never get any narrower than this setting. Wrapping takes care of everything else.</p>
  32. <h2 id="dealing-with-remainders-of-1">Dealing with remainders of 1</h2>
  33. <p>If I have a 4 column grid with 8 children present then I add a child element, that element will constitute the whole of the bottom row. Ideally, I’d like to move things around so that this is not the case. By harnessing the ability of my elements to grow and incorporating nome <code>nth-child</code> magic, I can essentially borrow elements from the penultimate row to distribute the elements towards the end of the grid more reasonably. I can also do this without negatively affecting other quantities of elements producing more than a single remainder or no remainder at all.</p>
  34. <p>In the following example, I’ve adapted the first example in this article.</p>
  35. <pre class="prettyprint"><code class=" hljs css"><span class="hljs-class">.parent</span> <span class="hljs-rules">{&#13;
  36. <span class="hljs-rule"><span class="hljs-attribute">display</span>:<span class="hljs-value"> flex</span></span>;&#13;
  37. <span class="hljs-rule"><span class="hljs-attribute">flex-flow</span>:<span class="hljs-value"> row wrap</span></span>;&#13;
  38. <span class="hljs-rule">}</span></span>&#13;
  39. &#13;
  40. <span class="hljs-class">.child</span> <span class="hljs-rules">{&#13;
  41. <span class="hljs-rule"><span class="hljs-attribute">flex</span>:<span class="hljs-value"> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">25</span>%</span></span>;&#13;
  42. <span class="hljs-rule">}</span></span>&#13;
  43. &#13;
  44. <span class="hljs-class">.child</span><span class="hljs-pseudo">:nth-last-child(2)</span><span class="hljs-pseudo">:nth-child(4n)</span> <span class="hljs-rules">{&#13;
  45. <span class="hljs-rule"><span class="hljs-attribute">min-width</span>:<span class="hljs-value"> <span class="hljs-number">33</span>%</span></span>;&#13;
  46. <span class="hljs-rule">}</span></span></code></pre>
  47. <p>This selector expression in the last block targets any element that falls in the fourth and final column (<code>4n</code>) which is simultaneously the penultimate element ”“ that is, the element before the long, single remainder. By bumping this element’s width to <code>33%</code>, it’s forced down into the final line, leaving the penultimate line with just three elements. </p>
  48. <p>The result is the elimination of the single element and a dynamic grid that resolves into a line of 4, then 3, then 2 children when there is a single remainder. No matter how many child elements are present, the grid never ends with a single element row.</p>
  49. <p>Try adding and removing items in <a href="http://codepen.io/heydon/pen/zvNxZN">this codePen demo</a> and experimenting with different numbers of columns. The basic formula for the selector is <code>.child:nth-last-child(2):nth-child([number of columns]n)</code>. The width you set has to be somewhere between the base width and the base width for the grid if it had one less column. So, if there are five columns, the width set in this override should be between 20% and 25%.</p>
  50. <h2 id="controlled-chaos">Controlled chaos</h2>
  51. <p>In the last example, I singled out a child element based on its index using the algebraic <code>:nth-child</code> and <code>nth-last-child</code>. Because of my Flexbox configuration’s insistence of filling the available space according to its element growth and wrapping features, I could do this safe in the knowledge that I would not produce an incomplete, gap-ridden grid.</p>
  52. <p>Based on this principle, I can arbitrarily change the widths of any grid children I like. I can get quite expressive with this and build in some algorithmic asymmetry.</p>
  53. <pre class="prettyprint"><code class=" hljs css"><span class="hljs-class">.child</span><span class="hljs-pseudo">:nth-child(3n)</span> <span class="hljs-rules">{&#13;
  54. <span class="hljs-rule"><span class="hljs-attribute">width</span>:<span class="hljs-value"> <span class="hljs-number">33.333</span>%</span></span>;&#13;
  55. <span class="hljs-rule">}</span></span>&#13;
  56. &#13;
  57. <span class="hljs-class">.child</span><span class="hljs-pseudo">:nth-child(5n)</span> <span class="hljs-rules">{&#13;
  58. <span class="hljs-rule"><span class="hljs-attribute">width</span>:<span class="hljs-value"> <span class="hljs-number">50</span>%</span></span>;&#13;
  59. <span class="hljs-rule">}</span></span>&#13;
  60. &#13;
  61. <span class="hljs-class">.child</span><span class="hljs-pseudo">:nth-child(7n)</span> <span class="hljs-rules">{&#13;
  62. <span class="hljs-rule"><span class="hljs-attribute">width</span>:<span class="hljs-value"> <span class="hljs-number">66.666</span>%</span></span>;&#13;
  63. <span class="hljs-rule">}</span></span></code></pre>
  64. <p>By using prime numbers (3, 5 and 7) to augment the child elements’ width at intervals, any perceived regularity in the layout can be easily diminished. However, the layout never <em>breaks</em> as such thanks to our go-to wrapping and growth settings. Be sure to try out the <a href="http://codepen.io/heydon/pen/GpbQdP">codePen demo</a> for this one and experiment with different, superimposed <code>nth-child</code> intervals.</p>
  65. <h2 id="gutter-tactics">Gutter tactics</h2>
  66. <p><em>(This is an edit to the original article after <a href="https://medium.com/@StuCoxMedia/how-would-you-suggest-adding-a-gutter-with-this-pattern-6e176fb6b4e9#.2ts42s545">Stu Cox</a> started a discussion about how one might deal with gutters.)</em></p>
  67. <p>Gutters are, put simply, the gaps between grid children to space them. I’m not absolutely certain what the “flexy” way to add gutters to any of my described grids would be, but I am aware of one technique which would create gutters that do not break when wrapping produces remainders.</p>
  68. <pre class="prettyprint"><code class=" hljs css"><span class="hljs-class">.parent</span> <span class="hljs-rules">{&#13;
  69. <span class="hljs-rule"><span class="hljs-attribute">display</span>:<span class="hljs-value"> flex</span></span>;&#13;
  70. <span class="hljs-rule"><span class="hljs-attribute">flex-flow</span>:<span class="hljs-value"> row wrap</span></span>;&#13;
  71. <span class="hljs-rule"><span class="hljs-attribute">margin-left</span>:<span class="hljs-value"> -<span class="hljs-number">0.5</span>rem</span></span>;&#13;
  72. <span class="hljs-rule"><span class="hljs-attribute">margin-right</span>:<span class="hljs-value"> -<span class="hljs-number">0.5</span>rem</span></span>;&#13;
  73. <span class="hljs-rule">}</span></span>&#13;
  74. &#13;
  75. <span class="hljs-class">.child</span> <span class="hljs-rules">{&#13;
  76. <span class="hljs-rule"><span class="hljs-attribute">flex</span>:<span class="hljs-value"> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">25</span>%</span></span>;&#13;
  77. <span class="hljs-rule"><span class="hljs-attribute">box-sizing</span>:<span class="hljs-value"> border-box</span></span>;&#13;
  78. <span class="hljs-rule"><span class="hljs-attribute">padding</span>:<span class="hljs-value"> <span class="hljs-number">0</span> <span class="hljs-number">0.5</span>rem <span class="hljs-number">1</span>rem</span></span>;&#13;
  79. <span class="hljs-rule">}</span></span></code></pre>
  80. <p>It is important that I treat each item equally, rather than trying to anticipate things with nth children, because any of my items can potentially grow or wrap. Hence the <code>padding: 0.5rem 1rem</code> on all <code>.child</code> elements. This produces a <code>1rem</code> gutter around each child, no matter how they wrap or grow.</p>
  81. <p>All that’s left is to compensate for the redundant 0.5rem space for left-most and right-most children, facilitated by the left and right negative margins on the parent (Stu arrived at this notion). Depending on the vertical rhythm of your page, you may want to remove the redundant bottom margin of the last child row too, with margin-bottom: -1rem on the parent too. There is <a href="http://codepen.io/heydon/pen/EVqwOW">a codePen to play with</a>.</p>
  82. <h2 id="conclusion">Conclusion</h2>
  83. <p>I hope that this short article has given you something to think about regarding the way Flexbox handles and tolerates dynamic content, allowing you to tersely code robust yet expressive layouts. With Flexbox, for the first time, we’re afforded something akin to true grid <em>systems</em>; grids which govern themselves, freeing us to focus on content creation and aesthetics.</p>
  84. </div>