Relative padding and vh units - bug or spec misunderstanding?

Larry picture Larry · Sep 6, 2013 · Viewed 10.5k times · Source

DEMO

Sometimes I'll create a square (or any rectangle, really) that will respect its ratio at any size using a method similar to this method.

What I want:

  • to prevent the square extending outside of the viewport on devices with a small height i.e. mobile phone in landscape

Proposed solution

  • limit width of square to a percentage of viewport height using max-width: 90vh
  • expect ratio to be respected

CSS

    .square {
      position: relative;
      height: 0;
      padding-bottom: 100%;
      overflow: hidden;
    }

    .square-inner {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
    }

    .mw { max-width: 90vh;} /* solution, but makes things break */

HTML

    <div class="square mw">
        <div class="square-inner"></div>
    </div>

What should happen

  • in viewports with small heights, the square should be a max width of 90% of the viewport height

What actually happens:

  • when viewport height is less than width of square:
    • width is constrained as per vh value
    • height is calculated from width of square had it not been constrained to vh
    • we get a vertically long rectangle

The spec says that the relative value is calculated from the 'containing block', which to me seems as though it should be the current width of the container.

Browser behaviour:

  • Chrome 29.0.1547.65: as described
  • Firefox 23.01: as described
  • Opera: does not respect vh at all Not validated with Opera 16+

Am I interpreting the spec incorrectly, or is this a possible bug in implementation by browser vendors?

Answer

apaul picture apaul · Sep 6, 2013

The problem is in using both lengths in % and vh.

Try this:

Working Example

* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  font-family: sans-serif;
  font-weight: 100;
}

.neat {
  width: 50%;
  max-width: 600px;
  min-width: 320px;
  margin: 0 auto;
}

.col {
  float: left;
  padding: 2rem;
  width: 90vh;    /*** Important bit changed width:50%; to width:90vh; ***/
  max-width: 50%; /*** Important bit added max-width:50%; ***/
}

.square {
  position: relative;
  height: 0;
  padding-bottom: 100%;
  overflow: hidden;
}

.square-inner {
  position: absolute;
  background-color: #333333;
  color: white;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 1.5rem;
}

.mw {
  max-width: 90vh;
}