Invisible cursors in Google Chrome with CSS
We’re currently working on a couple of touchscreen projects for the National Museum of Australia. Traditionally, due to their requirement for rich and seamless interactivity (and an eventual online delivery), we would build these interactives in Flash. What makes them a little more interesting this time around is we’re building them in HTML and leveraging WebKit’s CSS-fu for handling transitions and animations. I’ll be putting together a more comprehensive write-up on that process (with code!) in a little while, but for now let’s talk about one of the many little issues that come with using a different technology.a
Some context
The specified delivery platform for these projects is a PC running Windows XP, so we’ve chosen Chrome as the browser to run them in. Chrome uses the WebKit rendering engine, so we get the power of CSS transitions (now with hardware acceleration), and it can run in kiosk mode for a fullscreen experience.
It works quite well, except that mouse hiding doesn’t work as expected. “What mouse?” you ask. Well, for those not familiar with this technique, before these newfangled touch devices become popular, most touchscreens were simply monitors that pass the user input onto the computer as a proxy for a mouse. This means you still have a cursor and you’re limited to the various single-touch gestures that a mouse can normally handle. I’m sure there are ways to hide the cursor on a universal basis on Windows XP, but I wanted a solution that was contained to the browser. Were we able to use an OS X machine, we could use something like Plainview for WebKit goodness combined with a kiosk mode and mouse hiding options, but in Chrome on Windows it’s a little trickier.
All the other browsers I’ve tested in — Chrome (OS X), Safari, and Firefox 3+ — the solution to this problem is incredibly simple. Add this to your CSS file and you’re done:
* { cursor: none !important }
Unfortunately this doesn’t work for Chrome on Windows, probably because none
is not actually a valid/standard value for the cursor
property. All is not lost. If you were paying careful attention to the spec you’ll have noticed that you can specify a URI as the value for your cursor
. We can simply make a transparent image and use that as the value with a fallback to none for other browsers!
* { cursor: url(cursor.png), none !important }
Well, not quite. Chrome seems to have a bug with transparent cursors. Perhaps it’s intentional to ensure your mouse is always visible, but here’s what happens when you use a completely transparent image for your cursor in Chrome. I’ve used an 100px × 100px image so you can see it:
Chrome converts 100% transparent pixels to 100% black. Not exactly what we were after. However, there is a solution: as long as the pixels in your cursor .png
aren’t 100% transparent, Chrome will render them correctly. I’ve created a 1px × 1px image with a single back layer at 1% opacity. Here it is in-situ:
Perfect, or at least near enough that it’s impossible to tell. If you look very closely you can see the 1-pixel image I’ve used, but it’s almost undetectable.
TL;DR
Problem: Chrome on Windows doesn’t like cursor: none
or 100% transparent images for custom cursors. Solution: create your custom cursor image at 1% opacity.
* Note: Don’t take this code as a completely cross-browser solution to getting invisible cursors. I’ve only looked at WebKit for this project, so you may run into issues on other browsers (Internet Explorer, predictably, requires a specific .cur
file format for custom cursors).*