21.10.11

Resizing backgrounds in CSS

The CSS background-image property allows web developers to add backgrounds to parts of pages, but only at their original sizes. CSS 3 added background-size to resize background images, and I implemented it in Gecko. But I couldn’t implement it for SVG backgrounds, because Gecko didn’t support them. When support arrived, nobody updated the background sizing algorithm for vector images’ increased flexibility. I’d hoped to prevent this omission by adding “canary” tests for background-size-modified SVG backgrounds. But I miswrote the tests, so background-size wasn’t updated for SVG-in-CSS.

Since I’d inadvertently allowed this mess to happen, I felt somewhat obligated to fix it. Starting with Firefox 8, Firefox will properly render backgrounds which are vector images, at the appropriate size required by the corresponding background-size. To the best of my knowledge Gecko is the first browser engine to properly render vector images in CSS backgrounds.

If the image specifies a size and isn’t modified by contain or cover, that size wins.

With no other information, the rendered size is the corresponding size of the background area.

Note that sizing only cares about the image’s dimensions and proportions, or lack thereof. A vector-based image with fixed dimensions will be treated identically to a pixel-based image of the same size.

This image is 100 pixels high but lacks a width, and it has an intrinsic ratio of 3:4. The ratio ensures that its width:height ratio is always 3:4, unless it’s deliberately scaled to a disproportionate size. (One dimension and an intrinsic ratio is really no different from two dimensions, but it’s still useful as an example.)

This image has no width or height, but it has an intrinsic ratio of 1:1. Think of it as a program icon: always square, just as usable at 32×32 or 128×128 or 512×512.

In the examples below, all enclosing rectangles are 300 pixels wide and 200 pixels tall. Also, all backgrounds have background-repeat: no-repeat for easier understanding. Note that the demos below are all the expected rendering, not the actual rendering in your browser. See how your browser actually does on this demo page, and download an Aurora or Beta build (or even a nightly) to see the demo rendered correctly.

Now consider the rules while moving through these questions to address all possible background-size values.

Is the background-sizecontain or cover?

cover makes the picture as small as possible to cover the background area. contain makes the picture as large as possible while still fitting in the background area. For an image with an intrinsic ratio, exactly one size matches the cover/fit criteria alone. But for a vector image lacking an intrinsic ratio, cover/fit is insufficient, so the large/small constraints choose the resulting rendered size.

Is the background-sizeauto or auto auto?

Per rule 2, rendering must preserve any intrinsic ratio.

If we have an intrinsic ratio, any dimension (or both) fixes the other, and we’re done. If we have an intrinsic ratio but no dimensions, then per rule 4, we use the background area — but see rule 2! To preserve the intrinsic ratio, the image is rendered as if for contain.

Whee, that’s a mouthful!

Anything else to know?

In rewriting the sizing algorithm, I was confronted with the problem of how to resize CSS gradients (distinct from gradients embedded in SVG images), which CSS treats as an image subtype.

Our previous sizing algorithm happened to treat gradients as if they were a special image type which magically inherited the intrinsic ratio of their context. Thus if the background were resized with a single length, the gradient would paint over a proportional part of the background area. Other resizing would simply expand them to cover the background area.

CSS 3 Image Values specifies the nature of the images represented by gradients: they have no dimensions and no intrinsic ratio. Firefox 8 implements these semantics, which are a change from gradient rendering semantics in previous releases. This will affect rendering only in the case where background-size is auto <length> or <length> auto (and equivalently, simply <length>). Thus if you wish to resize a gradient background, you should not use a length in concert with auto to do so, because in that case rendering will vary across browsers.

Conclusion

SVG background images have now become more powerful than you can possibly imagine. If the SVG has fixed dimensions, it’ll work like any raster image. But SVG goes beyond this: if an SVG image only has partial fixed dimensions, the final rendering will respect that partial dimension information. Proportioned images will remain proportioned (unless you force them out of proportion); they won’t be disproportionally stretched just because the background area has particular dimensions. Images with a dimension but no intrinsic ratio will have the dimension used when the background is auto-sized, rather than simply have it be ignored. These aren’t just bugfixes: they’re new functionality for the web developer’s toolbox.

04.08.09

This one’s on Mozilla’s new implementation of the CSS 3 property background-size, which I wrote because I wanted the functionality and because I figured it would be fun and informative to do so (and it was both).

20.02.09

This is what the Acid2 test looks like in the very very super-duper-latest Firefox builds (slated for the version after 3.1, mind, not for 3.1):

Acid2 in bleeding-edge Mozilla, to be seen in the next Firefox release after 3.1

Bug, you say? No! The last test in row 13 tests that this CSS doesn’t apply:

.parser { background: red pink; }

Two colors for a background as valid CSS? Surely you jest!

Actually, we don’t. CSS3 says background-color takes two colors, one for normal use and one for use if a corresponding background-image doesn’t load. The red ends up getting used as the background color, and the yellow that would have been present if the property hadn’t applied no longer shows. CSS3 makes previously-invalid CSS valid, and that’s okay, because CSS error handling is explicitly designed to allow it.

So no, “failing” Acid2 for now isn’t fail, it’s WIN. Hopefully the test will be updated soon so that that particular rule is invalid again.

Incidentally, the specification for this changed recently, which I presume is why WebKit/Safari/Chrome et al. don’t fail. I don’t know whether they implement the fallback color process or not, presumably not given how they handle this testcase, which should show a green square if CSS3 background colors are implemented to latest spec, but in any case they won’t trigger it even tho they’ve implemented a bunch of other parts of the CSS3 backgrounds spec.

Update: As Dan notes in comments, the once-invalid syntax is now invalid again, as fallback color support has been removed from CSS3 partially over syntax concerns and partially over its lack of generalizability to other cases in CSS. Now that support for fallback colors has been removed from trunk, Acid2 in bleeding-edge Firefox now displays as it was intended to display.