107

This might sound like a silly question.

If I use this CSS snippet for regular displays (Where box-bg.png is 200px by 200px);

.box{
    background:url('images/box-bg.png') no-repeat top left;
    width:200px;
    height:200px
}

and if I use a media query like this to target retina displays (With the @2x image being the high-res version);

@media (-webkit-min-device-pixel-ratio: 2), 
(min-resolution: 192dpi) { 

    .box{background:url('images/box-bg@2x.png') no-repeat top left;}
}

Do I need to double the size of the .box div to 400px by 400px to match the new high res background image?

Adrift
  • 58,167
  • 12
  • 92
  • 90
Dean Elliott
  • 1,503
  • 3
  • 17
  • 16
  • What is the dimension of images/box-bg@2x.png? Please put it to the question to be absolutely clear. – Tomas Jan 24 '14 at 22:55

4 Answers4

195

Do I need to double the size of the .box div to 400px by 400px to match the new high res background image

No, but you do need to set the background-size property to match the original dimensions:

@media (-webkit-min-device-pixel-ratio: 2), 
(min-resolution: 192dpi) { 

    .box{
        background:url('images/box-bg@2x.png') no-repeat top left;
        background-size: 200px 200px;
    }
}

EDIT

To add a little more to this answer, here is the retina detection query I tend to use:

@media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (   min--moz-device-pixel-ratio: 2),
only screen and (     -o-min-device-pixel-ratio: 2/1),
only screen and (        min-device-pixel-ratio: 2),
only screen and (                min-resolution: 192dpi),
only screen and (                min-resolution: 2dppx) { 

}

- Source

NB. This min--moz-device-pixel-ratio: is not a typo. It is a well documented bug in certain versions of Firefox and should be written like this in order to support older versions (prior to Firefox 16). - Source


As @LiamNewmarch mentioned in the comments below, you can include the background-size in your shorthand background declaration like so:

.box{
    background:url('images/box-bg@2x.png') no-repeat top left / 200px 200px;
}

However, I personally would not advise using the shorthand form as it is not supported in iOS <= 6 or Android making it unreliable in most situations.

Turnip
  • 35,836
  • 15
  • 89
  • 111
  • any tip you'd offer for setting the background-size for a full-page background? i know the x component's width, but how about the y? – Randy L Oct 09 '13 at 19:04
  • 3
    @theOther In that case you probably want to be using `background-size: cover;` . This will maintain aspect ratio whilst "covering" the whole background with image. – Turnip Oct 10 '13 at 08:11
  • 4
    If you’d like, you can integrate the `background-size` property into the `background` like so: `background: url('images/box-bg@2x.png') no-repeat top left / 200px 200px`. Note that browsers that don’t support `background-size` will ignore this rule. – Liam Newmarch Nov 02 '13 at 21:30
  • 2
    @LiamNewmarch I wouldn't recommend that myself as Android doesn't seem to understand the shorthand form – Turnip Jan 03 '14 at 16:09
  • @3rror404 ah okay, fair enough. Thanks! – Liam Newmarch Jan 05 '14 at 16:44
  • @3rror404 In your opinion, is it negligible that the `background-size` property is not supported by some browsers, given that most devices utilising retina displays will be using modern browsers? – shennan Jan 21 '14 at 22:43
  • There should probably be the `.box {}` code in the second code block (extended retina query) too? Now it is empty and it is confusing. – Tomas Jan 24 '14 at 22:54
  • @LiamNewmarch, great find; and, btw, it's a better idea to use it that way, especially if you have a hidpi image, since either the whole statement is accepted, or none of it, without a hidpi image of 100px x 100px (in hiDPI pixels) ending up being zommed in to its actual size of 200px x 200px (in non-DPI pixels). – cnst Apr 13 '16 at 20:21
16

Here's a solution that also includes High(er)DPI (MDPI) devices > ~160 dots per inch like quite a few non-iOS Devices (f.e.: Google Nexus 7 2012):

.box {
    background: url( 'img/box-bg.png' ) no-repeat top left;
    width: 200px;
    height: 200px;
}
@media only screen and ( -webkit-min-device-pixel-ratio: 1.3 ),
       only screen and (    min--moz-device-pixel-ratio: 1.3 ),
       only screen and (      -o-min-device-pixel-ratio: 2.6/2 ), /* returns 1.3, see Dev.Opera */
       only screen and (         min-device-pixel-ratio: 1.3 ),
       only screen and ( min-resolution: 124.8dpi ),
       only screen and ( min-resolution: 1.3dppx ) {

       .box {
           background: url( 'img/box-bg@2x.png' ) no-repeat top left / 200px 200px;
       }

}

As @3rror404 included in his edit after receiving feedback from the comments, there's a world beyond Webkit/iPhone. One thing that bugs me with most solutions around so far like the one referenced as source above at CSS-Tricks, is that this isn't taken fully into account.
The original source went already further.

As an example the Nexus 7 (2012) screen is a TVDPI screen with a weird device-pixel-ratio of 1.325. When loading the images with normal resolution they are upscaled via interpolation and therefore blurry. For me applying this rule in the media query to include those devices succeeded in best customer feedback.

Volker E.
  • 5,911
  • 11
  • 47
  • 64
  • 1
    An image 2x of each dimension has exactly 4x the pixels (e.g., can theoretically be expected to be 4x in size), whereas 1.325 * 1.325 supports only 1.755625 increase in pixels. I think the quality of the image would be lost either way with 1.325dppi, but if you go HiDPI, then the client will simply have to wait longer for page load, will have higher power consumption resizing the image (and Nexus 7 tables are quite known for random reboots), and consume more bandwidth. So, I'd recommend sticking with `@2x` only being served to `2dppx`+ clients. – cnst Apr 13 '16 at 20:53
3

If you are planing to use the same image for retina and non-retina screen then here is the solution. Say that you have a image of 200x200 and have two icons in top row and two icon in bottom row. So, it's four quadrants.

.sprite-of-icons {
  background: url("../images/icons-in-four-quad-of-200by200.png") no-repeat;
  background-size: 100px 100px /* Scale it down to 50% rather using 200x200 */
}

.sp-logo-1 { background-position: 0 0; }

/* Reduce positioning of the icons down to 50% rather using -50px */
.sp-logo-2 { background-position: -25px 0 }
.sp-logo-3 { background-position: 0 -25px }
.sp-logo-3 { background-position: -25px -25px }

Scaling and positioning of the sprite icons to 50% than actual value, you can get the expected result.


Another handy SCSS mixin solution by Ryan Benhase.

/****************************
 HIGH PPI DISPLAY BACKGROUNDS
*****************************/

@mixin background-2x($path, $ext: "png", $w: auto, $h: auto, $pos: left top, $repeat: no-repeat) {

  $at1x_path: "#{$path}.#{$ext}";
  $at2x_path: "#{$path}@2x.#{$ext}";

  background-image: url("#{$at1x_path}");
  background-size: $w $h;
  background-position: $pos;
  background-repeat: $repeat;

  @media all and (-webkit-min-device-pixel-ratio : 1.5),
  all and (-o-min-device-pixel-ratio: 3/2),
  all and (min--moz-device-pixel-ratio: 1.5),
  all and (min-device-pixel-ratio: 1.5) {
    background-image: url("#{$at2x_path}"); 
  }
}

div.background {
  @include background-2x( 'path/to/image', 'jpg', 100px, 100px, center center, repeat-x );
}

For more info about above mixin READ HERE.

Syed
  • 15,657
  • 13
  • 120
  • 154
3

You could use image-set(). This CSS function currently has limited support, but it does work. More about it on MND web docs, but here is an example how it works:

.box {
  background-image: -webkit-image-set(
    url('images/box-bg.png') 1x,
    url('images/box-bg@2x.png') 2x); /* Temporary fallback for Chrome and Safari browsers until they support 'image-set()' better */
  background-image: image-set(
    url('images/box-bg.png') 1x,
    url('images/box-bg@2x.png') 2x);
}

The '1x' and '2x' are the resolution here. '2x' for retina displays.