March 1, 2021

Hover and Pointer Media Queries

Hover and Pointer events on mobile are ...hard.

The basics of Hover and Pointer events

Hover and Pointer events on mobile platforms have always been hard to deal with. The CSS :hover pseudo-selector is great for styling content when the mouse is hovered on devices with fine pointer control. But on mobile devices and tablets, it's not so great. Check out the example below on a device with a fine pointer. Hover the element. You'll see its styles change. Try to get that same effect on mobile. It requires the user to click, then the :hover state is applied. It then requires a second click to actually do the action the element is binded to.

See the Pen Hover States: Desktop by Nate Northway (@the_Northway) on CodePen.

This pattern means that two clicks are required to do the action the element is binded to on mobile. Without this knowledge, mobile users may believe it's not working. They may reload the page or give up trying. Maybe not. Wouldn't it be nice if there were ways to fix this?

Enter the JavaScript

There are JS methods to handle this. Using JS, devs can determine whether the device has a touchscreen and apply different styles based on that. Most commonly, a class is added to the <body> tag, which then gets used in the CSS to change the styles. In the example below, JS tests for touch support, then applies a class of .no-touch to the body tag if there is not touch support. The CSS applies hover state styles to .no-touch .btn elements. I think this is the best way to deal with this (for now) because it's mobile first. It doesn't block functionality when clicked on a mobile device.

See the Pen Hover States: With Touchscreen Detection by Nate Northway (@the_Northway) on CodePen.

But is that it?

Well...no. There are more seperations to be made. For example, you could add conditional styles if it is a touch device. There are more complications, too. There are laptops and desktops with touch screens. Also, using that JS is a little wonky because IE and Firefox mobile don't support it. Chrome and Safari do, which makes it work in a lot of cases, but definitely not all. To fix all of this, Level 4 Media Queries were introduced.

Level 4 Media Queries

Level 4 media queries include hover, any-hover, pointer, and any-pointer. They are designed to give the developer some information about the hovering capability of the browser. They test the user's primary input mechanism.

@media (hover): The 'hover' feature

The 'hover' media feature queries the user's ability to hover over elements on the page with the primary pointing device. If a device has multiple pointing devices, the feature reflects the 'primary' pointing device, as determined by the user agent. It can test two values:

  • none: indicates that the primary pointing device can't hover or that there is no pointing device. This would include touchscreens and screens that use a stylus.
  • hover: Indicates that the primary pointing device can easily hover. Examples include mice and other devices that physically point at the screen, like the Wii controller

@media (pointer): The 'pointer' feature

The pointer media feature queries the presence and accuracy of a pointing device, like a mouse. If a device has multiple pointing devices, it reflects the 'primary' pointing device, determined by the user agent.

  • none: The primary input mechanism doesn't include a pointing device
  • coarse: The primary input mechanism includes a pointing device with limited accuracy.
  • fine: The primary input mechanism includes an accurate pointing device.

@media (any-hover) and (any-pointer)

The any-hover and any-pointer media features are the same as hover and pointer but they have a broader scope. They correspond to all the capabilities of the pointing devices available to the user.
any-hover tests if any hover-capable pointing devices are present - not whether or not any of the pointing devices are hover incapable. It doesn't take into account any non-pointing device inputs, like d-pads or keyboard-only inputs that aren't hover capable.
any-pointer is used to test the presencce and accuracy of pointing devices. It doesn't take into account any non-pointing device inputs also in use, and can't be used to test for the presence of other input devices - like d-pads or keyboard-only inputs that don't move an on-screen pointer.

Use Cases

That was a lot of words and the first time reading it, I was a bit confused but could see where everything was going. I think it's time for some code, of course, with comments.

  
    @media (hover: hover) {
      /* Targets devices with a primary input that responds to hover fully, like a mouse or stylus */
    }
    @media (hover: none) {
      /* Targets devices that don't respond to hover at all, like a touchscreen */
    }
    @media (pointer: coarse) {
      /* Targets devices with a primary input that has a coarse pointer, like a touchscreen */
    }
    @media (pointer: fine) {
      /* Targets devices with a primary input that has a fine pointer, like a mouse or stylus */
    }
    @media (pointer: none) {
      /* Targets devices with a primary input that does not have a pointer (like what? I don't know. Keyboards?) */
    }
  

Using Level 4 Media Queries

Although this is still a working draft, support is pretty good according to caniuse, which states that the latest versions of Edge, Firefox, Chrome, Safari, Android Browser, Chrome for Android, and iOS Safari all support it. But I don't know if I'd start using this feature just yet. Since it is a working draft, the spec could change. I'd wait until these features are actually codified and introduced as a rec before using them, and until then, stick with the JS solution (if it works for you).
Here are some links that talk a bit more about this:
Javascript Kit
Media Queries Level 4 editor's draft @mezoistvan on Medium