How to Overflow a Background Image Using CSS3

September 1st, 2011 Permalink

You can file this one under the heading of CSS3 tricks, and / or abuse of the ::before and ::after selectors. Why would anyone want or need to make a background image overflow the bounds of the element it is applied to? That might be left as an exercise for the reader, or you can skip to the end of this post for an example. In any case, here's how to do it. We'll start with an unassuming 500 x 40 pixel image:

And an unsuspecting div styled as follows:

<style type="text/css">
.exampleBox {
   width: 300px;
   height: 40px;
   margin: 15px 0 15px 30px;
   border: 1px solid black;
   background-color: #cccccc;
   -moz-box-shadow:inset 0 0 10px white;
   -webkit-box-shadow:inset 0 0 10px white;
   box-shadow:inset 0 0 10px white;
}
</style>
<div class="exampleBox"></div>

Now we set the wide image as a background for the not-so-wide box, which has the predictable result:

<style type="text/css">
.withBackground {
  background-image: url(https://www.exratione.com/assets/color_background.png);
}
</style>
<div class="exampleBox withBackground"></div>

The image is truncated. This is an apparent dead end; without changing the width of the box there is no way to show the rest of the background. By definition a background image stops at the bounds of the box - you can tell it to consider the border to be included in those bounds, but that is as far as it goes. So how do you make a background image overflow an element? The answer is that you cannot do it, but you can fake it as follows:

<style type="text/css">
.withOverflowingBackground {
   position: relative;
   background-color: transparent;
}
.withOverflowingBackground::before {
   content: " ";
   position: absolute;
   width: 500px;
   height: 40px;
   z-index: -1;
   background-image: url(https://www.exratione.com/assets/color_background.png);
}
</style>
<div class="exampleBox withOverflowingBackground"></div>

The box-shadow and border are left in to make it clear that yes, the original div is still there and still 300 pixels wide. So what did we just accomplish with the use of the ::before selector and content directive? In effect we told the browser to create a new absolutely-positioned element inside the div with a space character as content, and then style that element with the full-sized background. Creating HTML elements with CSS3 directives may seem perverse, but it in fact opens up some efficient and compact ways of dealing with a range of otherwise annoying issues. Look on this as shorthand for adding an additional absolutely-positioned element and styling it.

The ability to fake the overflow of a background image might seem like a dumb application of CSS3 capabilities ("just change the size of the element already"), but there is at least one HTML5 mobile application framework - Jo - in which this is an elegant solution to visual problems that arise in the interaction between view element height, view background, and Javascript code that manages the flick-scrolling effect. The flick-scroll of a view element is going to briefly expose a length of screen real estate that extends above or below the height of the content before the content-bearing view snaps back into place. The user will see the edge of the view element going past, and that might look ugly, depending on the design.

The scroll event is governed by the height of the content-bearing element - and so if you want a background that moves with the scrolled view and also extends into that real estate above and below the view element revealed by flick-scrolling, then you need to use the technique demonstrated in the example above. Extending the height of the view container element to show more of the background will just add empty space to the view that the use can scroll into, and beyond that will be the real estate that you were trying to cover up.

The alternatives to using the ::before selector for the Jo case are either (a) a much less elegant injection of absolutely positioned HTML elements as strings directly into the joCard or joScroller objects that contain the view, or (b) changing the scroll management Javascript under the hood. Both are possible, but maintaining the solution in CSS is, I think, better all round in this instance.