Use flexbox and maintain an aspect ratio even though content is sized differently
Asked Answered
P

2

19

There are a lot of questions on SO about maintaining the aspect ratio of an element (with flexbox or without). However, my problem is slightly different as I want to override the aspect ratio of a child image element:

  1. Make sure the image covers the element (object-fit: cover) completely
  2. Make sure the element is 1:1 (i.e. a perfect circle)
  3. Make sure that the overflowing image is hidden

In other words, the image has to behave as if it was the background of an element (I can't use them as background images though) of which the aspect ratio is always 1:1 and responsive.

In the example below everything works fine except that the <a> elements adapt to their image descendant. But I want them to maintain a 1:1 ratio so I get perfect circles. (The middle one of the first row has to be larger than the rest, though.)

The HTML can't change, but I can use modern CSS properties such as object-fit and flexbox. (As long as recent versions of Chrome/Firefox support it.)

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.img-gallery {
  background: #fafafa;
  padding: 24px;
  min-width: 320px;
  width: 90%;
  margin: 0 auto;
}

.img-gallery .row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-around;
  align-items: center;
}

.img-gallery a {
  display: block;
  text-decoration: none;
  background-image: linear-gradient(60deg, #004494 0%, #7db9e8 78%, #c2dfed 100%);
  overflow: hidden;
  border-radius: 50%;
  padding: 3px;
  flex: 1;
  margin: 0 24px;
  transition: padding 200ms;
}

.img-gallery a:hover,
#s_country .img-gallery .row:first-of-type a:nth-child(2):hover {
  padding: 0;
}

.img-gallery a:hover span {
  transform: scale(1.25);
}

.img-gallery .row:first-of-type a:not(:nth-child(2)) {
  width: 30%;
  width: calc((60% - 96px) / 2);
}

.img-gallery .row:first-of-type a:nth-child(2) {
  flex: 2;
  padding: 4px;
}

.img-gallery span {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 50%;
  position: relative;
  overflow: hidden;
  transition: transform 250ms;
  z-index: 2;
}

.img-gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.img-gallery span::before {
  content: "";
  background-image: linear-gradient(60deg, transparent 48%, #ffc5e7 100%);
  width: 100%;
  height: 100%;
  position: absolute;
  z-index: 2;
  border-radius: 50%;
  opacity: .72;
}
<div class="img-gallery">
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/b3/9c/54/b39c54776074d07ee0b567826768730a.jpg" id="img-1-3"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d6/df/51/d6df512a2f15f517767b4d82d2d97a4c.jpg" id="img-1-4"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/ec/a9/dd/eca9dd106a04cdbee399870252ef711f.jpg" id="img-1-5"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/7d/01/19/7d0119a2fec989e208f288326c7cad0f.jpg" id="img-1-6"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d8/c3/32/d8c332d09b03673845b2e92a48816233.jpg" id="img-1-7"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/88/3b/dd/883bddab14168f5f0807fec021002d8d.jpg" id="img-1-8"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/8e/4f/bb/8e4fbb89b155d15521b80d1baf9290d1.jpg" id="img-1-9"></span></a>
  </div>
</div>

Code illustrating when Terry's code does not work: landscape pictures.

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.img-gallery {
  background: #fafafa;
  padding: 24px;
  min-width: 320px;
  width: 90%;
  margin: 0 auto;
}

.img-gallery .row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-around;
  align-items: center;
}

.img-gallery a {
  display: block;
  text-decoration: none;
  background-image: linear-gradient(60deg, #004494 0%, #7db9e8 78%, #c2dfed 100%);
  overflow: hidden;
  border-radius: 50%;
  padding: 3px;
  flex: 1;
  margin: 0 24px;
  transition: padding 200ms;
}

.img-gallery .row:first-of-type a:not(:nth-child(2)) {
  width: 30%;
  width: calc((60% - 96px) / 2);
}

.img-gallery .row:first-of-type a:nth-child(2) {
  flex: 2;
  padding: 4px;
}

.img-gallery span {
  height: 0;
  display: block;
  border-radius: 50%;
  position: relative;
  padding-bottom: 100%;
  overflow: hidden;
  transition: transform 250ms;
  z-index: 2;
}

.img-gallery img {
  width: 100%;
  object-fit: cover;
  transition: transform 250ms;
}

.img-gallery a:hover img {
  transform: scale(1.25);
}

.img-gallery span::before {
  content: "";
  background-image: linear-gradient(60deg, transparent 48%, #ffc5e7 100%);
  width: 100%;
  height: 100%;
  position: absolute;
  z-index: 2;
  border-radius: 50%;
  opacity: .72;
}
<div class="img-gallery">
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/b3/9c/54/b39c54776074d07ee0b567826768730a.jpg" id="img-1-3"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d6/df/51/d6df512a2f15f517767b4d82d2d97a4c.jpg" id="img-1-4"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/ec/a9/dd/eca9dd106a04cdbee399870252ef711f.jpg" id="img-1-5"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/7d/01/19/7d0119a2fec989e208f288326c7cad0f.jpg" id="img-1-6"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d8/c3/32/d8c332d09b03673845b2e92a48816233.jpg" id="img-1-7"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/13/7c/3d/137c3d3bd9f25aa9d2677136d9336d74.jpg" id="img-1-8"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/8e/4f/bb/8e4fbb89b155d15521b80d1baf9290d1.jpg" id="img-1-9"></span></a>
  </div>
</div>
Photography answered 16/5, 2017 at 18:28 Comment(2)
All the images are portrait mode ? (that is, height > width)Bowden
@Bowden No, the dimensions cannot be known in advance.Photography
M
15

To maintain the aspect ratio of responsive elements, you can use the padding technique.
Note that you shouldn't use percentages on padding bottom/top for flex children, see here for more info.
You can make a grid of responsive squares and add border-radius to make them circles.

For the images, the object-fit: cover; property does exactly what you need : keep the image original aspect ratio and covering the element completely.
I changed the first image to a landscape image to show this technique works also with those.

Here is an example of how you can achieve your aim (I stripped some of your CSS out to keep the demo simple) :

*,*::before,*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.img-gallery {
  background: #fafafa;
  padding: 24px;
  min-width: 320px;
  width: 90%;
  margin: 0 auto;
}

.img-gallery .row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-around;
  align-items: center;
}

.img-gallery a {
  display: block;
  position:relative;
  text-decoration: none;
  background-image: linear-gradient(60deg, #004494 0%, #7db9e8 78%, #c2dfed 100%);
  overflow: hidden;
  border-radius: 50%;
  flex: 1;
  margin: 24px;
}
.img-gallery a::before{
  content:'';
  display:block;
  padding-bottom:100%;
}


.img-gallery .row:first-of-type a:not(:nth-child(2)) {
  width: 30%;
  width: calc((60% - 96px) / 2);
}

.img-gallery .row:first-of-type a:nth-child(2) {
  flex: 2;
}

.img-gallery span {
  position:absolute;
  top:3px;left:3px;right:3px;bottom:3px;
  border-radius: 50%;
  overflow: hidden;
  transition: transform 250ms;
}

.img-gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition:transform 0.5s;
}
.img-gallery a:hover img{
  transform:scale(1.25);
}
<div class="img-gallery">
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://farm7.staticflickr.com/6217/6216951796_e50778255c.jpg" id="img-1-3"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d6/df/51/d6df512a2f15f517767b4d82d2d97a4c.jpg" id="img-1-4"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/ec/a9/dd/eca9dd106a04cdbee399870252ef711f.jpg" id="img-1-5"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/7d/01/19/7d0119a2fec989e208f288326c7cad0f.jpg" id="img-1-6"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d8/c3/32/d8c332d09b03673845b2e92a48816233.jpg" id="img-1-7"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/88/3b/dd/883bddab14168f5f0807fec021002d8d.jpg" id="img-1-8"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/8e/4f/bb/8e4fbb89b155d15521b80d1baf9290d1.jpg" id="img-1-9"></span></a>
  </div>
</div>

Note that you will need to add vendor prefixes for the transition and transform properties depending on the browsers you need to support. See canIuse for transforms and transitions.

Magpie answered 31/5, 2017 at 6:41 Comment(3)
The differences between this and Terry's answer are not immediately obvious. The asker should probably supply some landscape images for use so you can show if yours works with them (which the asker requires).Memorize
good point @BoltClock. I changed the first image to a landscape image to show that the object-fit : cover; property works with those too.Magpie
Ooh, good stuff. I also did not notice that the asker had indeed already edited to add a landscape image.Memorize
C
2

It's quite simple: you can use the padding-bottom: 100% hack to force a 1:1 aspect ratio. According to CSS specification, vertical margins/paddings, when declared in percentages, references the parent width. The logic behind this was never clearly explained, but I suspect is to prevent circular calculations.

Anyway, now you know that you can use padding-bottom: <percentage> to force a fixed aspect ratio ;) now we simply apply that to .img-gallery span. Remember to set its height to 0, since we no longer need to specify height:

.img-gallery span {
  height: 0;
  display: block;
  border-radius: 50%;
  position: relative;
  padding-bottom: 100%;
  overflow: hidden;
  transition: transform 250ms;
  z-index: 2;
}

p/s: I am not very sure why you are setting the padding to 0 on the :nth-child(2n) element on hover, so I have removed it for now.

Here is a proof-of-concept example:

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.img-gallery {
  background: #fafafa;
  padding: 24px;
  min-width: 320px;
  width: 90%;
  margin: 0 auto;
}

.img-gallery .row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-around;
  align-items: center;
}

.img-gallery a {
  display: block;
  text-decoration: none;
  background-image: linear-gradient(60deg, #004494 0%, #7db9e8 78%, #c2dfed 100%);
  overflow: hidden;
  border-radius: 50%;
  padding: 3px;
  flex: 1;
  margin: 0 24px;
  transition: padding 200ms;
}

.img-gallery .row:first-of-type a:not(:nth-child(2)) {
  width: 30%;
  width: calc((60% - 96px) / 2);
}

.img-gallery .row:first-of-type a:nth-child(2) {
  flex: 2;
  padding: 4px;
}

.img-gallery span {
  height: 0;
  display: block;
  border-radius: 50%;
  position: relative;
  padding-bottom: 100%;
  overflow: hidden;
  transition: transform 250ms;
  z-index: 2;
}

.img-gallery img {
  width: 100%;
  object-fit: cover;
  transition: transform 250ms;
}

.img-gallery a:hover img {
  transform: scale(1.25);
}

.img-gallery span::before {
  content: "";
  background-image: linear-gradient(60deg, transparent 48%, #ffc5e7 100%);
  width: 100%;
  height: 100%;
  position: absolute;
  z-index: 2;
  border-radius: 50%;
  opacity: .72;
}
<div class="img-gallery">
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/b3/9c/54/b39c54776074d07ee0b567826768730a.jpg" id="img-1-3"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d6/df/51/d6df512a2f15f517767b4d82d2d97a4c.jpg" id="img-1-4"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/ec/a9/dd/eca9dd106a04cdbee399870252ef711f.jpg" id="img-1-5"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/7d/01/19/7d0119a2fec989e208f288326c7cad0f.jpg" id="img-1-6"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/d8/c3/32/d8c332d09b03673845b2e92a48816233.jpg" id="img-1-7"></span></a>
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/88/3b/dd/883bddab14168f5f0807fec021002d8d.jpg" id="img-1-8"></span></a>
  </div>
  <div class="row">
    <a href="#" title="Show large image"><span><img itemprop="image" src="https://s-media-cache-ak0.pinimg.com/originals/8e/4f/bb/8e4fbb89b155d15521b80d1baf9290d1.jpg" id="img-1-9"></span></a>
  </div>
</div>
Clyte answered 16/5, 2017 at 21:27 Comment(1)
Unfortunately this runs into the issue I had before: this only works if all pictures have a portrait orientation, which by coincidence all my examples had. In my OP I added an example of a landscape picture, showing that this does not work as intended. (The span is not covered with the image.)Photography

© 2022 - 2024 — McMap. All rights reserved.