Setting the CSS blur filter to zero in WebKit on a retina screen

Published
Written by
John Kavanagh

Setting the amount of blur on an element to zero via CSS should be as simple as passing a zero-value into the filter property. For devices with Apple's new Retina screen however, that's not the case...

Google Chrome error robot illustration by Sebastien Gabriel

Despite fairly wide-spread uptake in the development and browser community, the filter property is still a relatively immature part of CSS (the module specification still currently only a working draft). In the case of using the blur() filter, the (draft) specification notes that a CSS length parameter is used to define the radius of the Gaussian function applied:

The passed parameter (the radius) defines the value of the standard deviation to the Gaussian function. The parameter is specified a CSS length, but does not accept percentage or negative values.

Filter Effects Module Level 1, W3C Working Draft, 26 November 2013

In layman's terms: blur() needs a numerical value, with or without a unit value (px, em, etc); the bigger the value, the more blurry the element you apply it to will render (assuming the correct use of vendor prefixes and browser support).

At present prevailing wisdom suggests the pre-emptive use of each of the major vendor prefixes. whether those vendors currently support the property or not. As an example, if you wanted to add a 10px blur to an element using CSS, you would end up with a piece of CSS that looks something like this:


.blur-me{
	filter: blur(10px);
	-webkit-filter: blur(10px);
	-moz-filter: blur(10px);
	-o-filter: blur(10px);
	-ms-filter: blur(10px);
}

Of course you could also include an SVG filter in there as a final fallback, but given how minimal browser support currently is for all filter effects if it simply wouldn't be acceptable for your development to degrade back to non-blurred for the 56.59%1 of browsers that don't currently support it, then you might need to consider another route that doesn't use the effect at all.

One of the nice things about filters is that they can be animated and used in conjunction with CSS transitions. I use it myself on this website to blur the main website content when the mobile fly-in navigation is opened, which is where I came across a stumbling block. My CSS looked something like this:


.page{
	filter: blur(0);
	-webkit-filter: blur(0);
	-moz-filter: blur(0);
	-o-filter: blur(0);
	-ms-filter: blur(0);
}

.nav-open .page{
	filter: blur(3px);
	-webkit-filter: blur(3px);
	-moz-filter: blur(3px);
	-o-filter: blur(3px);
	-ms-filter: blur(3px);
}

All fairly straightforward: .page would have no blur (blur(0)) by default, and a 3px blur when switched via the parent class (.nav-open).

Although this method worked just as expected, it also revealed an interesting bug in the way that WebKit handles display on Apple's retina screens: when viewed on my MacBook Pro the blur never quite fully extinguished itself, retaining a very subtle blur:

Screenshot of blur(0) in Chrome on a retina device

However, using filter: none still reverted (correctly) back to no blur at all, although that's not an ideal solution if you're using other filters on that same element!

Reverting to the incessant knowledge of Stack Overflow, the answer seems to be that this is a known bug in WebKit (although from all my searching, I couldn't find any mention of it online) and the only answer aside from using none is a combination of using either -webkit-transform: translateZ(0); or webkit-backface-visibility: hidden;. Both will work, although either may have other unintended effects depending on your situation. Tread lightly.

The nice thing is that since this appears to be soley a WebKit issue, there isn't any need for any other vendor prefixes, although I do opt to include the non-prefixed version of the fix too for forward-compatibility:


.page{
	-webkit-transform: translateZ(0)
	transform: translateZ(0)
	filter: blur(0);
	-webkit-filter: blur(0);
	-moz-filter: blur(0);
	-o-filter: blur(0);
	-ms-filter: blur(0);
}

.nav-open .page{
	filter: blur(3px);
	-webkit-filter: blur(3px);
	-moz-filter: blur(3px);
	-o-filter: blur(3px);
	-ms-filter: blur(3px);
}

What I find most interest about this is that both the workarounds above share one thing in common: both are used as sort-of hacky ways of boosting transition and filter performace by enabling hardware-accelerationin WebKit browsers2. Perhaps it is nothing more than a coincidence, but I do wonder whether the reason the zero-setting works when using hardware-acceleration on these high-density screens relates to the GPU being designed specifically to process at these sub-pixel/zero levels whereas the software acceleration appears to round the blur effect to somewhere inbetween 0px and 1px.

Comments powered by Disqus
Top