Faire
Faire

Reputation: 1021

CSS background fixed + cover stretches the picture (not covering properly)

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?

Upvotes: 2

Views: 2342

Answers (2)

IronFlare
IronFlare

Reputation: 2322

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.

Upvotes: 1

Phil
Phil

Reputation: 433

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.

Upvotes: 0

Related Questions