1

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?

1
  • 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. Commented Jul 30, 2022 at 14:01

2 Answers 2

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>

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.