1

I am using fixed background images in my ReactJS website. The image I am using in header area is getting weirdly stretched (zoomed-in, only a small part of picture is visible) even though the same CSS properties for different elements work well.

App.js

return (
<div id="main">
    <div id="header">
    ...
    </div>
...
    <div id="bg-img1" className="background_image">
...

App.css

#header {
  background-image: url("img/svatba.jpg");
  background-position: center center;
  background-size: cover;
  background-attachment: fixed;
  height: 300px;
  background-repeat: no-repeat;
}
...
.background_image {
  background-size: cover;
  background-position: center;
  text-align:center;
  min-height: 326px;
  background-repeat: no-repeat;
}
#bg-img1 {
  background: url('img/svatba2.jpg');
  background-attachment: fixed;
  background-repeat: no-repeat;
}
...

Now the image in the "header" element shows up zoomed, not covering the viewport as it should. The image in the "bg-img1" element is displayed properly.

What am I missing?

Charlene Vas
  • 723
  • 1
  • 9
  • 21
Faire
  • 706
  • 1
  • 9
  • 31
  • When you say it's *not covering the viewport*, what do you mean? How should it look? – IronFlare Aug 23 '19 at 13:21
  • when using fixed you will consider the viewport as reference and no more the element – Temani Afif Aug 23 '19 at 13:21
  • IronFlare: I expect it to stretch its wide to fit exactly the viewport. This image is stretched too much, e.g. I see only a small part of the image, which is heavily zoomed-n. – Faire Aug 28 '19 at 10:46

2 Answers2

0

When you set the background-size to "cover", you are telling it to take that image and resize it (or "zoom it in" as you're saying) so that it covers the entire section (here #header).

If your hope is to have a header 300px high that spans the whole width of the page without losing any portions of your image, You would need to serve an image that shares the same proportions as your header.

For example, if your header is 300 x 1000, you could load an image of the same dimensions or of 120 x 400, keeping an aspect ratio of 3:10.

Phil
  • 383
  • 1
  • 4
  • 18
  • I must have not made myself clear - I fully expect the picture to be zoomed in exactly so that the width of the picture will be equal to the width of the viewport, which is not happening. I can see only partial width (and partial height!) of the picture. E.g. if the picture would be say 100 x 50px, I would only see 50 x 30px. All picture but this one behave correctly. I am starting to suspect that the picture is somehow faulty. – Faire Aug 28 '19 at 11:06
0

background-attachment

The way that background-attachment: fixed is commonly used is as a way to prevent a background image from moving relative to the viewport whenever the document is scrolled.

Note: For the purposes of this explanation, the viewport can be thought of as being equivalent to the browser window, although this isn't strictly the case.

CSS does this by basically taking the element's background image and attaching it to the viewport instead of to the element itself. Since the viewport doesn't change its position when the user scrolls, the image will be statically positioned relative to the window. So far, so good.


background-size

Where we get into trouble with attachment is when we try to combine it with background-size: cover (or contain). Because background-attachment has already sent the background image to the viewport, any background position changes made through the CSS become relative to the viewport.

This is normally fine, but it means that when you try to use either a percentage value or a predefined size operator like cover, the background will also be sized to the viewport.

While writing up this summary, I discovered that this behavior is mentioned in the MDN documentation. The only problem is that it's just two sentences jammed in the middle of the percentage paragraph in the Values section of the background-size page. Yikes.


Demo

I've created an interactive demo to show the results of this behavior. To view it, click here.

The demo will display four panels, each with a different combination of sizing and attachment. Move your mouse over each of these panels to see how the background image is positioned in the container, and what's been hidden.

Fun fact: I made over 300 revisions to this demo before I was comfortable calling it done :P


Conclusion

In one of your comments below, you said (emphasis mine):

Cover was the culprit - but I have a little idea why. All the images are landscape. The elements have min-height. I expected that the cover will fix the width to the viewport, and that contain would fix the height to the element height. Instead I see the cover zooming absurdly (not matching any of the dimensions) and contain matches the width (which is what I wanted). But why?

CSS often subverts expectations, and this is no different. For the cases below, assume we're using background-repeat: no-repeat.

  • cover scales the background image so it fits the element's largest dimension exactly and overflows the smaller one. This will generally cause it to be much larger than the element, showing only a portion of the image.
  • contain sizes the background image so it fits the element's smallest dimension and leaves blank space on either side of the image in the larger dimension.

But when you use background-attachment: fixed...

  • When you use cover, what you're actually seeing is the image being scaled to match the height of the viewport, since the height is smaller. With a landscape image, the height of the image will be scaled to the height of the viewport, which is why it appears so large.
  • When you use contain, the image is scaled to match the width of the viewport. If your element takes up the full width of the viewport, this will cover the element, cutting off the image's height, if necessary.

If you want to size the image using element-relative cover or contain, your two options, essentially, are to remove background-attachment: fixed, or to resize the source image so that your background-size declaration isn't necessary. Unfortunately, no CSS solution currently exists to enable attachment and keyword-based sizing at the same time.

IronFlare
  • 2,287
  • 2
  • 17
  • 27
  • Thank you for the advice, however the problem is that in spite of `background-size: cover;` I can see only relatively small part of the image (not covering either of its dimensions) because it is heavily zoomed in. What I expect is that with landascape pictures the picture dimension is set so that the width of the picture equals the width of the viewport. That is not happening. – Faire Aug 28 '19 at 10:49
  • @Faire Ohhh, I see what’s happened. With `background-attachment: fixed`, the image sizes itself relative to the browser viewport, not to the element that bounds it. This is a byproduct of how attachment works — by drawing all positioning data (including size) from the root element. Unfortunately, there’s no way that I know of to have both fixed attachment and `background-size: cover` relative to the element. I’ll update my answer shortly. – IronFlare Aug 28 '19 at 20:07
  • Cover was the culprit - but I have a little idea why. All the images are landscape. The elements have min-height. I expected that the cover will fix the width to the viewport, and that contain would fix the height to the element height. Instead I see the cover zooming absurdly (not matching any of the dimensions) and contain matches the width (which is what I wanted). But why? – Faire Aug 29 '19 at 06:44
  • @Faire Sorry for the delay! I've completely rewritten my answer and included an in-depth explanation of the behavior, an interactive demo, and a summary of how it affects your project. Please let me know if anything's still unclear. – IronFlare Sep 04 '19 at 20:13