0

I'm using the ::before with content: ''; padding-bottom: 100% trick to try to enforce a 1:1 aspect ratio on images. I've also added overflow: hidden. However, as you can see from the screenshot, the images are overflowing outside of the square I've created inside ::before. I don't want the images to stretch at all, so all of them have object-fit: contain applied.

I've followed various threads and a CSS Tricks article on aspect ratio.

How can I ensure that images fit neatly into that square and do not overflow at all?

EDIT: Code sandbox

Here's the code:

Item.tsx

<StyledItemWrapper
  className={item.animation}
  onClick={() => {
    clickItem(item);
  }}
>
  <picture>
    <img src={item.image} alt={item.title} />
  </picture>
  <StyledItemInfo>
    <p>{item.title}</p>
    <p>{item.description}</p>
    <p>${item.price.toFixed(2)}</p>
  </StyledItemInfo>
  <Button onClick={() => handleAddToCart(item)}>Add To Cart</Button>
</StyledItemWrapper>

Item.styles.ts

export const StyledItemWrapper = styled.div`
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  position: relative;
  border: 1px solid lightblue;
  border-radius: 20px;
  &::before {
    display: block;
    overflow: hidden;
    padding-bottom: 100%;
    content: "";
  }
  picture {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    max-height: 50%;
    img {
      border-radius: 20px 20px 0 0;
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }

  button {
    border-radius: 0 0 20px 20px;
  }
`;

export const StyledItemInfo = styled.div`
  font-family: Arial, Helvetica, sans-serif;
  padding: 1rem;
  height: 100%;
`;

enter image description here

crevulus
  • 1,658
  • 12
  • 42

1 Answers1

1

The problem is that I was applying styled to the StyledItemWrapper which wraps the whole card and not just the image, so any padding etc was affecting the layout of the whole card.

The padding and pseudoselector hack for aspect ratio only works if you apply it to a dedicated image container. So, that's what I made:

Item.tsx

const Item: React.FC<Props> = ({ item, handleAddToCart, clickItem }) => (
  <StyledItemWrapper
    className={item.animation}
    onClick={() => {
      clickItem(item);
    }}
  >
    <StyledVisualWrapper> // new styled component
      <picture>
        <img src={item.image} alt={item.title} />
      </picture>
    </StyledVisualWrapper>
    <StyledItemInfo>
      <p>{item.title}</p>
      <p>{item.description}</p>
      <p>${item.price.toFixed(2)}</p>
    </StyledItemInfo>
    <Button onClick={() => handleAddToCart(item)}>Add To Cart</Button>
  </StyledItemWrapper>
);

Item.styles.ts

export const StyledItemWrapper = styled.div`
  display: flex;
  justify-content: space-between;
  flex-direction: column;
  border: 1px solid lightblue;
  border-radius: 20px;

  button {
    border-radius: 0 0 20px 20px;
  }
`;

export const StyledVisualWrapper = styled.div`
  position: relative; // move from StyledItemWrapper to here
  max-height: 50%;
  &::before {
    display: block;
    overflow: hidden;
    padding-bottom: 100%;
    content: "";
  }
  picture {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    img {
      border-radius: 20px 20px 0 0;
      width: 100%;
      height: 100%;
      object-fit: contain;
    }
  }
`;
crevulus
  • 1,658
  • 12
  • 42