The fixed background attachment breaks when using translateX(0)

I'm facing an issue with a scrolling element that has a fixed background:

.my-element {
   background: url(/foo.png) no-repeat right bottom;
   background-attachment: fixed;
}

Everything works perfectly, until I apply a translate transform (even a slight one) to it for an animation. This causes the background to become non-fixed and anchor to the bottom of .my-element, which scrolls out of view:

.my-element {
   background: url(/foo.png) no-repeat right bottom;
   background-attachment: fixed;
   transform: translateX(0); // this disrupts the "fixed" behavior
}

Although a similar question was asked about this here with answers stating the bug has been fixed and was limited to Firefox, I am experiencing it in both Firefox and Chrome.

Is there a workaround for this issue? Or is there an alternative way to achieve the same behavior without using the background-attachment property? Thank you!

edit: I have also created a jsfiddle to illustrate the problem: https://jsfiddle.net/mozges0k/7/

Answer №1

Trying to find a workaround was quite challenging for me. I attempted to separate the background image into its own container and apply position: sticky to it, thinking it could work, but unfortunately, it didn't pan out.

Another idea popped into my head: what if I extracted the transform into its own class and removed that class once the animation was completed? It appeared that removing the transform allowed the background-attachment to function properly, even at runtime. This way, the background-attachment could work its magic while the animation played smoothly.

<div class="my-element animate">Lorem ipsum</div>
.my-element {
    background: url(/foo.png) no-repeat right bottom;
    background-attachment: fixed;
}

.animate {
    transform: translateX(0);
}
function animationFinished() {
    const target = document.querySelector('.animate');
    target.classList.remove('animate')
}

Although not as straightforward as I had hoped, this could potentially be a viable solution. I would love to hear your thoughts on this approach. Best of luck!

Answer №2

Insight

After delving deeper into the subject, I have discovered that this behavior is not a glitch but rather it adheres to the standards.

Initially, the expected behavior was in line with what you (and most of us) believed: setting background-attachment: fixed should fix the background relative to the viewport.

However, it appears that implementing the fixing behavior becomes quite challenging when the transform property is applied. As a result, the specification authors resolved to modify the spec for simplicity.

As per the current specification:

Fixed backgrounds on the root element are influenced by any specified transform for that element. Whereas, for other elements with an enforced transform (i.e., having a transform on them or any of their ancestors), setting the "background-attachment" property to "fixed" is considered equivalent to "scroll".

This essentially indicates that transform overrides the effect of background-attachment: fixed.

Resolution

My suggestion would be to utilize a fixed-size scroll container, matching the viewport's dimensions, with overflow: scroll. This container will act as the background carrier, enabling you to apply transformations to it.

Since it mirrors the viewport's size, vertical scrolling won't exhibit any variance. Yet, you can still manipulate it using transform: translateX(n) at will.

var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.'

const scrollContainer = document.querySelector('.scroll-container')
const contentContainer = document.querySelector('.content-container')

const p = document.createElement('p')
p.innerText = text.repeat(20)

contentContainer.appendChild(p)

const btn = document.getElementById('btn')

btn.addEventListener('click', () => {
  scrollContainer.style = 'transform: translateX(-1000px);'
})
body {
  margin: 0;
  padding: 0;
}

.scroll-container {
  box-sizing: border-box;
  padding: 10px;
  border-radius: 10px;
  border: 2px solid red; 
  height: 100vh;
  width: 100vw;

  overflow: scroll;
  
  background: url("https://i.imgur.com/PcqkZxh.jpg") no-repeat;
  background-attachment: fixed;
  transition: 1s;
  transform: translateX(0);
}
<div class="scroll-container">
  <div class="content-container">
    <button id="btn">
      move
    </button>
  </div>
</div>

Answer №3

Should you be amenable to incorporating two containers (elements), then consider the following workaround:

Begin by creating a parent container for the element you intend to transform (translate).

--> You can set the position of the parent element as relative.

--> Create another container for the image and position it absolutely.

   .container-image {
  position: absolute;
  top: 0;
  left: 0;
  background: url("https://i.imgur.com/PcqkZxh.jpg") no-repeat;
  background-attachment: fixed;
  width: 100%;
  height: 100%;
  z-index: -1;
}

Now, you can easily apply any transform animation on the container with a fixed background image.

.foo {
  display: "none";
  flex-direction: "row";
}

.container-parent-broken {
  position: relative;
}

.container-broken {
  padding: 10px;
  border-radius: 10px;
  transform: translateX(0);
}

.container-image {
  position: absolute;
  top: 0;
  left: 0;
  background: url("https://i.imgur.com/PcqkZxh.jpg") no-repeat;
  background-attachment: fixed;
  width: 100%;
  height: 100%;
  z-index: -1;
}
<h1 style="text-align: center">
  Scroll to see the difference
</h1>

<div style="display: flex; column-gap: 20px;">

  <div class="container-parent-broken">
    <div class="container-image"></div>

    <div class="container-broken">
      <h1>
        This doesn't (translateX(0))
      </h1>
      <div>
        <p>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
          in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        </p>
        <p>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
          in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        </p>
        
        (Content continues...)

      </div>
    </div>

  </div>

</div>

Answer №4

One alternative approach to tackle this issue is by utilizing a grid layout within the containers.

Set up a grid with 1 column and 1 row, and then place both the image (contained in a div with a background image in this scenario) and the content (enclosed in another div) in the same row and column.

By applying position sticky to the image, it will stick when scrolling, while setting z-index: 0 on the content ensures it remains visible on top.

The key distinction is that the background image will start sticking only when the container reaches the top of the viewport and will stop sticking when scrolled past entirely.

You can now introduce translateX to animate the elements

.container-working, .container-broken {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
}

.container-broken {
  transform: translateX(0);
}

.container-image, .container-content {
  grid-row: 1;
  grid-column: 1;
}

.container-image {
  position: sticky;
  
  top: -10px; /* due to border-radius, it needs to stick slightly higher */
  border-radius: 10px;
  height: calc(100vh + 20px); /* increase height by double the border-radius */
    
  background: url("https://i.imgur.com/PcqkZxh.jpg") no-repeat;
  background-size: cover;
}

.container-content {
  z-index: 0;
  padding: 10px;
}
<h1 style="text-align: center">
Scroll to witness the difference
</h1>

<div style="display: flex; gap: 20px; position:relative;">
  <div class="container-working">
    <div class=container-image></div>
    <div class=container-content>
      <h1>
        This works (fixed bg)
      </h1>
      <div>
        <p>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
        </p>
        <p>
        Multiple paragraphs of text can be placed here to illustrate the content inside the working container.
        </p>
      </div>
    </div>
  </div>
  <div class="container-broken">
    <div class=container-image></div>
    <div class=container-content>
      <h1>
       This doesn't (translateX(0))
      </h1>
      <div>
        <p>
        Another set of paragraphs can be included here to demonstrate the content within the broken container.
        </p>
      </div>
    </div>
  </div>

</div>

Answer №5

Make sure to review the code as it is functioning correctly for me. I have encountered the same issue before. I included background-size: cover; and transform: translateX(0) in the keyframe and believe it will work for you as well.

.my-element {

.my-element { 
  padding: 10px; 
  border-radius: 10px; 
  background: url("https://i.imgur.com/PcqkZxh.jpg") no-repeat; 
  min-height: 500px; 
  background-attachment: fixed; 
  background-position: center; 
  background-repeat: no-repeat; 
  background-size: cover; 
  animation: mymove .1s alternate ; 
  -webkit-animation: mymove .1s alternate ; 
} 
@-webkit-keyframes mymove { 
  from { 
    transform: translateX(0); 
    -webkit-transform:translateX(0); 
  } 
} 
@keyframes mymove { 
  from { 
    transform: translateX(0); 
    -webkit-transform:translateX(0); 
  } 
} 
<h1 style="text-align: center"> 
Scroll to see the difference 
</h1> 
<div class="my-element"> 
   <h1> 
   This works (fixed bg) 
   </h1> 
  <div> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  <p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
  </p> 
  </div> 
  
  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Keep label at the forefront once input field is completed

Is it possible to keep my label positioned on top of the input field even after filling in data? I attempted using the valid function in CSS, but did not achieve the desired functionality. .txt_field input { width: 100%; padding: 0 5px; height: 4 ...

Using the HTML video tag to apply different background colors based on the individual machines being

My web application includes a video that is set to play against the same background color as the webpage. However, I am experiencing an issue where the video background appears differently on various machines using the same browser. What I would like to ac ...

Ensure that Bootstrap collapse is opened on desktop devices but closed on mobile devices

Is there a way to make a collapse open on desktop and closed on mobile using only Bootstrap? On Desktop: https://i.sstatic.net/IwHCv.png On Mobile: Here, the content should only be displayed when you click on the title of each group https://i.sstatic.n ...

Page designed for displaying small star ratings in JSP

In the realm of my star.jsp page, the style.css is its trusty companion, faithfully executing its duties. However, a desire has crept into my heart - I yearn for the stars to shrink in size. The Chronicles of My Code star.jsp <!DOCTYPE html> ...

Tips for customizing the color of the current date in the angular-material datepicker

I've created a function in angular-material to disable dates two days from now, but I'm struggling to change the color of the current date if it's disabled. The issue is that when the current date is disabled, it displays in a very light blu ...

To properly document the results, I must utilize a button to record the identification of a specific color

I'm working on a code that creates a clickable box which changes colors from black to green to white by going through different shades of green whenever the mouse is clicked. What I now need to do is implement a feature that displays the current shade ...

What are some ways to adjust red and green blocks using CSS?

One question that arises is how to create a version of a webpage where only the yellow block can slide up, while the red and green blocks remain fixed. Currently, the green block is treated with the following CSS: position:sticky; right:0px; top:100px; ...

adding a new transformation to the current properties

Is it possible to add a new transform property to existing ones? For instance, let's say I have a div.animation with the following style: .animation { transform: translateX(-50%) translateY(-50%); } Now I want to include transform: scale(1) to ...

Resizing an image proportionally using a div container with a child specifying the height dimension

When using a div element on its own, it automatically adjusts its height to fit its contents. I am trying to achieve a layout where a parent div contains two child elements: another div (or left-aligned image) and an unordered list (ul). The inner div (or ...

How can I ensure that buttons in a row utilize the full 100% of available space?

I have a row of buttons in my HTML code like this: HTML : <div class="buttons"> <div><input type="button"/></div> <div><input type="button"/></div> <div><input type="button"/></div& ...

Creating a dropdown navigation menu

I am trying to create a smooth slide down menu effect, but it seems to instantly expand with no transition. Can anyone help me troubleshoot this issue? const Container = styled.section` position: relative; overflow: hidden; ` const WrapperItems = st ...

When the click event occurs, add a class. After a delay using a timeout function, remove the added

I am currently using Jimdo and I have a section where I can edit all the codes, which is working fine except for one feature. Any assistance would be greatly appreciated! Here is the code that adds a class to an animated SVG image with its status set as d ...

Elevate a division within its container

Is there a way to make a smaller div positioned at the top when it's placed next to a larger one in the same container? I'm looking for a solution that doesn't involve using position-relative and manually adjusting the placement. Take a loo ...

Customize the appearance of the 'Update' or 'Publish' WordPress button using code from functions.php or style.css

Current issues with permissions to core files and stylesheets are prompting me to seek ways to customize the LOCATION and APPEARANCE of the 'Update / Publish' button in Wordpress CMS posts/pages. Any tips on how to achieve this despite the challe ...

Material User Interface, MUI, Modal, Back to Top Scroll按钮

After spending some time experimenting with scrollTop and the Dialog component (a fullscreen fixed modal) from Material-ui, I found that I couldn't quite get scrollTop to function properly. Whenever I clicked the "go down" button, it would either retu ...

Tips for switching out images depending on the time of day

Currently, I have a script that dynamically changes the background color of my webpage based on the time of day. However, I am facing issues trying to implement a similar functionality for replacing an image source. The current code is also time zone-based ...

picture located in the top corner of the page within the diva triangle shape

Is there a way to position my photo on the same level as a div and have it overlap slightly? <div id='triangle-topleft'> <img id="photo" src="photo.png"> </div> #triangle-topleft { width: 0; heigh ...

Looking to slide a form on top of an image carousel in Bootstrap - any tips?

How can I move this form over the "carousel"? <html> <head> <title>test</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http ...

Dynamic background image coupled with navigation buttons

Within the realm of Bootstrap 3, I've crafted a navbar that showcases a background image alongside a series of menu items positioned below said image: The following snippet of CSS handles the background image: .navbar-fixed-bottom .navbar-collapse ...

Is there a way to customize the color of a React component from a different source?

I am currently utilizing a React component library called vertical-timeline-component-react. <Fragment> <Timeline> <Content> <ContentYear startMonth="12" monthType="t ...