Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexbox tile layout with consistent margins and alignment

Tags:

html

css

flexbox

How might we achieve the following layout:

enter image description here

With the following conditions:

  1. The tiles of each row should be equal in height to the highest element in the row

  2. The last tile in the row should be flush with the parent container (no gap)

  3. The tiles can be different width ratios (3 times one-third or one-third + two-thirds, etc)

  4. The ordering of the tiles is unknown

  5. No grid framework

  6. Modern browsers only

And the following markup:

<div class="tile-wrapper">
  <div class="tile-container">
    <div class="tile third">1/3</div>
    <div class="tile third">1/3</div>
    <div class="tile third">1/3</div>
    <div class="tile two-thirds">2/3</div>
    <div class="tile third">1/3</div>
    <div class="tile sixth">1/6</div>
    <div class="tile sixth">1/6</div>
    <div class="tile third">1/3</div>
    <div class="tile sixth">1/6</div>
    <div class="tile sixth">1/6</div>
  </div>
</div>
like image 753
monners Avatar asked Jun 20 '15 04:06

monners


People also ask

How are Flex items aligned along the cross axis in a Flex container by default?

The align-items property will align the items on the cross axis. The initial value for this property is stretch and this is why flex items stretch to the height of the flex container by default.

Which property of flexbox is used to set child box alignment within the parent and box when extra space is available?

The justify-content property controls how that leftover space is used. Set justify-content: flex-end and the extra space is placed before the items, justify-content: space-around and it is placed either side of the item in that dimension, etc.

What is the difference between Flex and flexbox?

Flexbox is a purely css layout, which means the entire layout is manage and control at css level, no need to play with the markups. Flex model is capable to adjust its items height/width to best fill in the available space of container. It can expand or shrink to avoid overflow.

How do I use flexbox layout?

Align Contentflex-end: Rows are vertically aligned to the bottom (i.e., stacked from the bottom of the wrapper). center: Rows are centered in the wrapper vertically. stretch (default): In general, this property stretches the elements to utilize the entire vertical height of the wrapper.


2 Answers

This is an increasingly common layout that can be relatively easily achieved using flexbox and some clever container markup.

A common problem with tiled layouts is maintaining consistent spacing and alignment between tiles of differing sizes across multiple rows. Assuming we would like a 30px margin separating all tiles, we could simply set margin-right: 30px on all tiles and then use an nth-of-type selector to remove the margin-right of the last tile in the row.

However, this isn't possible when the relative sizes and order of the tiles is unknown. For example, a two-thirds tile next to a one-third tile won't conform to the same nth-of-type selector rule required to remove the margin of the last tile as a row consisting of three one-third tiles.

A clever way around this is to instead absord the right-most tile's margin into the width of the parent container. We can do with by setting the outer-most div's width to the desired width (in this case 90%) with overflow: hidden, and then set the inner-container's width to be 100% plus the width of the tile margin; width: calc(100% + 30px). This way, no matter what tile is in the right-most position of the row, the tile will sit flush with the edge of its container.

To ensure that the tiles then always fit as desired, we set their flex-basis to be the proportion of the row width we'd like them to occupy minus the tile margin; .third { flex: 0 0 calc(33.33% - 30px); /* for a 1/3 tile */}

Like so:

* {
  box-sizing: border-box;
}
.tile-wrapper {
  display: block;
  position: relative;
  width: 90%;
  margin: 0 auto;
  overflow: hidden;
}
.tile-container {
  display: flex;
  position: relative;
  width: calc(100% + 30px);
  flex-wrap: wrap;
}
.tile {
  display: inline-block;
  margin-right: 30px;
  margin-bottom: 30px;
  min-height: 200px;
  line-height: 199px;
  text-align: center;
  border: 1px solid black;
}
.two-thirds {
  flex: 0 0 calc(66.66% - 30px);
}
.third {
  flex: 0 0 calc(33.33% - 30px);
}
.sixth {
  flex: 0 0 calc(16.66% - 30px);
}
<div class="tile-wrapper">
  <div class="tile-container">
    <div class="tile third">1/3</div>
    <div class="tile third">1/3</div>
    <div class="tile third">1/3</div>
    <div class="tile two-thirds">2/3</div>
    <div class="tile third">1/3</div>
    <div class="tile sixth">1/6</div>
    <div class="tile sixth">1/6</div>
    <div class="tile third">1/3</div>
    <div class="tile sixth">1/6</div>
    <div class="tile sixth">1/6</div>
  </div>
</div>

Here's a Fiddle

NOTE: This will only work in modern browsers (Chrome, Safari, FF, IE11+). There's also a known bug in IE11 that requires you to use the long-hand flex-basis style attribute in order to set it to a calc() value.

like image 135
monners Avatar answered Sep 21 '22 19:09

monners


From the question:

  1. No grid framework

This question was posted in 2015, when CSS Grid Layout had weak browser support.

Since 2017, all major browsers support Grid Layout.

So, assuming that this requirement was posted for purposes of browser compatibility, I will look past it and provide an optimal (grid-based) solution easily supported by today's browsers.

body {
  margin: 0;
}

.tile-wrapper {}

.tile-container {
  display: grid;
  grid-template-rows: auto auto auto;
  grid-template-columns: repeat(6, 1fr);
  grid-gap: 30px;
}

.sixth {
  grid-column: span 1;
}

.third {
  grid-column: span 2;
}

.two-thirds {
  grid-column: span 4;
}

/* demo only */
.tile {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: lightgray;
  border: 1px solid gray;
}
<div class="tile-wrapper">
  <div class="tile-container">
    <div class="tile third">1/3</div>
    <div class="tile third">1/3<br>
    text<br>text<br>text</div>
    <div class="tile third">1/3</div>
    <div class="tile two-thirds">2/3</div>
    <div class="tile third">1/3<br>
    text<br>text<br>text<br>
    text<br>text<br>text<br>
    text<br>text<br>text</div>
    <div class="tile sixth">1/6<br>
    text<br>text<br>text<br>
    text<br>text</div>
    <div class="tile sixth">1/6</div>
    <div class="tile third">1/3</div>
    <div class="tile sixth">1/6</div>
    <div class="tile sixth">1/6</div>
  </div>
</div>

From the question:

With the following conditions:

  1. The tiles of each row should be equal in height to the highest element in the row. CHECK

  2. The last tile in the row should be flush with the parent container (no gap). CHECK

  3. The tiles can be different width ratios (3 times one-third or one-third + two-thirds, etc). CHECK

  4. The ordering of the tiles is unknown. OK

  5. No grid framework. N/A (see note above)

  6. Modern browsers only. CHECK

like image 22
Michael Benjamin Avatar answered Sep 17 '22 19:09

Michael Benjamin