0

I'm looking for a CSS solution that adapts to div contents, with the functionality of clip-path but dynamic. This is my code:

.background {
  background: yellow;
  text-align: center;
}

.text {
  display: inline-block;
  margin: 20px;
  padding: 20px;
  background: teal;
}
<div class="background">
<div class="text">
My text is in here
</div>
</div>

Yellow and teal are just used for illustration. I want to replace the yellow background with an image, but only show it in the teal area. The div.background spans the width of the browser, but I cannot make assumptions about the width of div.text. Can this be done with only CSS or does it require JS and dynamically setting background-position?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • An alternative solution would be attaching the `background-image` to .text instead and anchoring `background-position` to the parent element, but I do not think this is possible using just CSS. – Chris Conta Jul 30 '22 at 14:01

2 Answers2

1

Use a pseudo element that you make relative to the background element

.background {
  background: yellow;
  text-align: center;
  position: relative;
  z-index: 0;
}

.text {
  display: inline-block;
  color: #fff;
  margin: 20px;
  padding: 20px;
  clip-path: inset(0); /* clip to only text element */
}

.text:before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: url(https://picsum.photos/id/1056/800/600) center/cover;
}

/* to illustrate */
.text:hover {
  clip-path: none;
}
<div class="background">
  <div class="text">
    My text is in here
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
0

Here is one way of doing what you want through JS. The image is in the background element, and it is clipped according to the dimensions of the child element. There's a resize observer applied to the child element to trigger the calculation of the clipping mask whenever the dimensions of the child change.

I've added an animation to show how the clipping is calculated in real-time, but as you can see there is some slight stutter.

let text = document.querySelector('.text');
let bg = document.querySelector('.background');
let observer = new ResizeObserver(() => {
  calculateClipPath(bg, text);
})
observer.observe(text);

function calculateClipPath (parent, child) {
  parent.style.clipPath = `inset(
    ${child.offsetTop}px 
    ${parent.clientWidth - (child.offsetLeft + child.clientWidth)}px 
    ${parent.clientHeight - (child.offsetTop + child.clientHeight)}px 
    ${child.offsetLeft}px
  )`;
}
.background {
  background: url(https://c4.wallpaperflare.com/wallpaper/368/148/1024/flowers-roses-drawing-light-wallpaper-preview.jpg);
  text-align: center;
  position: relative;
}

.text {
  display: inline-block;
  margin: 20px;
  padding: 40px;
  width: 200px;
  animation: 3s infinite change;
}

@keyframes change {
  0% {
    width: 200px;
  }
  50% {
    width: 150px;
  }
  100% {
    width: 200px;
  } 
}
<div class="background">
<div class="text">
My text is in here
</div>
</div>

I'm still experimenting to see if there is a purely CSS version of the solution because that would always be smoother than the JS solution. If I can figure it out, I'll edit this answer and add it here

Omar Siddiqui
  • 1,625
  • 5
  • 18