Search code examples
cssclip-path

CSS clip-path to show vertical overflow above a button but not below


I have a button with an image to the left of the text and I would like to mask off the image so that the portion that hangs over the bottom of the button is hidden but the top overflow remains visible. From my research it seems like clip-path is one of the approaches for this, but I can't figure out how to determine the polygon values other than guessing/checking which hasn't been working out so well thus far. To add another wrench into the mix, I want the image to be slightly rotated.

.container {
  display:flex;
  width:100vw;
  height:100vh;
  align-items:center;
  justify-content:center;
}

button {
  width:450px;
  height:100px;
  background:black;
  font-family:sans-serif;
  font-size:30px;
  font-weight:bold;
  color:white;
  border:none;
  outline:none;
  position:relative;
  padding:20px 30px 20px 110px;
}

/* Image masked off below this line */
button:after {
  display:block;
  content:"";
  width:200%;
  height:1px;
  background:red;
  position:absolute;
  bottom:0;
  left:-50%;
}

.image {
  display:block;
  width:80px;
  height:120px;
  position:absolute;
  top:50%;
  left:30px;
  transform:translateY(-50%);
  /* Mask off bottom of image container */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 14% 100%);
}

.image img {
  transform:rotate(-6deg);
}
<div class="container">
  <button>
    <div class="image">
      <img src="https://placehold.co/80x120" />
    </div>
    Lorem ipsum dolor
  </button>
</div>

In the example above, the part of the image that falls below the red line would not be visible, but everything above it would be. Is there some trick to generating the clip-path values?


Solution

  • Consider having the .image container take the entire area of the button with extra top space for where the image peeks out above the button area:

    .container {
      display:flex;
      width:100vw;
      height:100vh;
      align-items:center;
      justify-content:center;
    }
    
    button {
      width:450px;
      height:100px;
      background:black;
      font-family:sans-serif;
      font-size:30px;
      font-weight:bold;
      color:white;
      border:none;
      outline:none;
      position:relative;
      padding:20px 30px 20px 110px;
    }
    
    /* Image masked off below this line */
    button:after {
      display:block;
      content:"";
      width:200%;
      height:1px;
      background:red;
      position:absolute;
      bottom:0;
      left:-50%;
    }
    
    .image {
      position:absolute;
      inset: -20px 0 0;
      overflow: hidden;
    }
    
    .image img {
      width:80px;
      height:120px;
      position:absolute;
      top: calc(50% + 10px);
      left:30px;
      transform:translateY(-50%) rotate(-6deg);
    }
    <div class="container">
      <button>
        <div class="image">
          <img src="https://placehold.co/80x120" />
        </div>
        Lorem ipsum dolor
      </button>
    </div>

    We transfer the positioning styling of the .image container to the <img> element and use overflow: hidden on the the .image container to clip the bottom of the <img>. The extra top spacing ensures the top of the image is not clipped. Clipping like this means we don't have to do any complex calculations with trigonometry. The 20px is an arbitrary distance that would be enough to show the top of the image.