1

I've created a layout below using display: grid, I've used the old padding-top trick to have fluid square divs, but I'm wondering if there is a better way of doing this, either with CSS Grid or Flexbox. The layout should look as is, and not use JS.

https://codepen.io/anon/pen/GWzavX

.o-container {
  margin: 0 auto;
  max-width: 1280px;
}

.o-grid {
  box-sizing: border-box;
  display: grid;
  grid-gap: 1rem;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, 1fr);
  max-width: 100%;
  padding: 0 1rem;
  width: 100%;
}

.o-grid__content {
  box-sizing: border-box;
  height: 100%;
  padding: 1rem;
  position: absolute;
  top: 0;
  width: 100%;
}

.o-grid__item {
  box-shadow: inset 0 0 0 1px red;
  position: relative;
}

.o-grid__item--tall {
  grid-row: span 2;
}

.o-grid__item--wide {
  grid-column: span 2;
  padding-top: 50%;
}
<div class="o-container">
  <div class="o-grid">
    <div class="o-grid__item o-grid__item--wide">
      <div class="o-grid__content">
        1. 2x1
      </div>
    </div>
    <div class="o-grid__item">
      <div class="o-grid__content">
        2. 1x1
      </div>
    </div>
    <div class="o-grid__item o-grid__item--tall">
      <div class="o-grid__content">
        3. 1x2
      </div>
    </div>
    <div class="o-grid__item">
      <div class="o-grid__content">
        4. 1x1
      </div>
    </div>
    <div class="o-grid__item">
      <div class="o-grid__content">
        5. 1x1
      </div>
    </div>
    <div class="o-grid__item">
      <div class="o-grid__content">
        6. 1x1
      </div>
    </div>
  </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
frontendbeast
  • 1,043
  • 4
  • 13
  • 28

3 Answers3

2

I've created a layout below using display: grid, I've used the old padding-top trick to have fluid square divs, but I'm wondering if there is a better way of doing this, either with CSS Grid or Flexbox.

The truth of the matter is that you're going to have to find another way to make this work, unless you're coding for Chrome only.

In CSS Grid, the padding-top trick fails in Firefox. The spec provides the reason:

6.4. Grid Item Margins and Paddings

Authors should avoid using percentages in paddings or margins on grid items entirely, as they will get different behavior in different browsers.

Percentage margins and paddings on grid items can be resolved against either:

  • their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
  • the inline axis (left/right/top/bottom percentages all resolve against width).

A User Agent must choose one of these two behaviors.

Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended to require that.

This issue exists in Flexbox, as well: Why doesn't percentage padding work on flex items in Firefox?

So your best bet at this point may be viewport percentage units.

From the spec:

5.1.2. Viewport-percentage lengths: the vw, vh, vmin, vmax units

The viewport-percentage lengths are relative to the size of the initial containing block. When the height or width of the initial containing block is changed, they are scaled accordingly.

  • vw unit - Equal to 1% of the width of the initial containing block.
  • vh unit - Equal to 1% of the height of the initial containing block.
  • vmin unit - Equal to the smaller of vw or vh.
  • vmax unit - Equal to the larger of vw or vh.

Here's a rough sketch: revised codepen

Community
  • 1
  • 1
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

Try replacing the padding with

height: calc(25vw - 1.5rem);

Or better yet, apply it to all the grid elements instead of just the first one:

.o-grid__item {
  box-shadow: inset 0 0 0 1px red;
  position: relative;
  height: calc(25vw - 1.5rem); /* allow for the spacing between squares */
}

.o-grid__item--tall {
  grid-row: span 2;
  height: calc(50vw - 1rem); 
}

.o-grid__item--wide {
  grid-column: span 2;
}
Blazemonger
  • 90,923
  • 26
  • 142
  • 180
  • The problem is though, it only works until the container width is reached. – frontendbeast Apr 03 '17 at 17:25
  • I'm not sure what you mean...? – Blazemonger Apr 03 '17 at 19:00
  • Compare [the first pen](https://codepen.io/anon/pen/GWzavX) to [this fork with your suggestions](https://codepen.io/anon/pen/JWzNar). The first issue is that the divs aren't square, and secondly they continue to get taller after the viewport width exceeds the container width (1280px). – frontendbeast Apr 03 '17 at 21:34
  • Just (1) adjust the height using `calc()` to reflect the actual width, and (2) use `@media screen` to add a `max-height` when you reach 1280px. – Blazemonger Apr 03 '17 at 23:22
0

There’s no “clean” way to achieve set aspect ratios for an element, grid or not. The zero height and percent-based padding trick is probably the most common solution to this.

The downside to this is it locks in the height of the element, so if the element is too small, its content will overflow.

I like a slightly different approach that uses a floated pseudo-element to achieve the same result, but also allows the element to grow to contain overflow if necessary:

.o-grid__item--wide::before {
  content: "";
  float: left;
  padding-bottom: 50%;
}
.o-grid__item--wide::after {
  clear: left;
  content: " ";
  display: table;
}

Using this inside a grid, you might want to get rid of your grid-template-rows declaration. Otherwise, if one element grows to contain the overflow, it will change the height of all the other grid rows as well.

keithjgrant
  • 12,421
  • 6
  • 54
  • 88