Introducing CSS Interaction Media Queries

How hovering on touchscreens has been fixed, and how to integrate hover and pointer media features

Ross Bulat
Jun 16 · 7 min read

CSS media queries give us the means of testing device properties, and to style the application appropriately depending on those properties.

@media is referred to as an “At-Rule”; it is the mostly developed and supported At-Rule, and accompanies @import, allowing stylesheet imports. Lesser-supported @supports and@document At-Rules also exist, giving stylesheets more capabilities to test browser-supported CSS properties, and restrict style based on URLs respectively.

When it comes to practical applications, there are some level 4 media query proposals that fix a long standing issue with hovering functionality. This article will explore these improved APIs, and will document how we can implement them into a stylesheet.

Note: The level 4 media query APIs talked about here at the time of writing are supported in all major browsers apart from Internet Explorer. It is very likely that these proposals will stick around, although their specification status is currently Candidate Recommendation. Even though the APIs talked about here could still change, it is unlikely given the major support and implementations in major browsers.

The problem: Hovering on touch devices

For a number of years, web developers have had to compromise on design choices when it comes to implementing hover effects. A touch input cannot hover over elements as a mouse would — a tap resembles a mouse click, whereas a long touch represents a scroll. There is no natural way to handle a hovering capability for touch, coming from a mouse pointer paradigm.

And with the majority of web traffic now coming from mobile devices, developers have had to put mobile-friendly designs first, and omit hovering effects that would otherwise exist on desktop-first web apps.

This is a design compromise, albeit a warranted one. Where hovering has been implemented, there are often trade-offs on touchscreen devices where UX errors often occur, including:

  • Activating hover effects when starting a scroll. When the user lifts their finger to stop the scroll, the activated element is still in a hover state
  • If an element has been accidentally hovered, the user must tap elsewhere in the app to disable that hover effect.
  • Tapping a custom link or button often activates a hover effect without actually activating an onClick handler. The user must tap again to trigger the event.

These are serious errors that have plagued developers for years.

Even though the earlier specifications of CSS media queries solved the responsiveness problem in relation to screen width and height, this was only one difference in form factor between the desktops, tablets and smartphones, and did not cater for the stark difference in input mechanisms that changed along with the device size.

A common workaround is to rely on device size to determine hover styles: to adopt a mobile first approach, and only code hover effects after a device reaches a certain width:

/* Mobile first button hovering based on device size 
* Not reliable - tablets are getting bigger!
*/
button {
color: black;
}
@media(min-width: 768px) {
button:hover {
color: grey;
}
}

This is no longer reliable, as larger-screen tablets are now more popular than ever, and are in some cases bigger than desktop screens with mouse input — the 12.9" iPad Pro for example is larger than the 12" MacBook screen. The screen size to input method paradigm has been broken.

To counter these issues, Level 4 Interaction Media Features have been introduced to the CSS specs. Let’s explore what they are and how to use them.

New Media Interaction Features: hover and pointer

As the title suggests, there have been some new media query capabilities introduced to CSS that allow us to fine-tune how we handle hover functionality.

The :hover selector is indeed still used to define a hover effect, but we can now determine when that hover takes place, based on the input mechanism of a device, using pointer and hover media features:

/* define styles based on input device pointer accuracy */
@media(pointer: <course| fine | none>) {
}
/* define styles for a hover-capable input mechanism */
@media(hover: <hover | none>) {
}

@media now supports pointer and hover features:

  • The pointer feature supports three values: none, course and fine, determining the input mechanism's pointing accuracy
  • The hover feature supports two values: none and hover, determining whether an input mechanism supports hover

These two features can either be used independently or in conjunction to fine-tune styling based on the input mechanism’s capabilities, whether that be a trackpad, touchscreen, basic and advanced stylus’s, and even game controllers such as the Nintendo Wii remote and Kinect controller.

The two other features: any-pointer and any-hover

pointer and hover refer to the primary input mechanism of a device. E.g. a trackpad for a laptop, or a touchscreen for a smartphone. However, we can also test the capability of all input mechanisms of a device, with any-pointer and any-hover:

  • any-pointer checks whether there is at least one pointing device either being course or fine, or none if no pointing device is available
  • any-hover checks whether at least one input device can conveniently hover over elements, with either hover or none values

Think of a touch screen laptop, or a Surface Pro tablet, that supports a range of input mechanisms. It is hard to predict which mechanism the user will be using.

If the developer wants to support a critical effect that depends on the capability to hover, or the capability to accurately point to an element, then any-hover and any-pointer will determine whether the device is capable of pulling it off. Doing so is risky though, and could compromise the user experience. Concretely, if a secondary input device is required to control an application, it should be obvious to the user — even if it means UI prompts.

Warning: Just because one of the input mechanisms outside of the primary input mechanism of a device could support hovering or accurate pointing, does not warrant a design relying on those features. As developers, we simply do not know which input mechanism the end-user will adopt, although it is a safe assumption to make that it will be the primary mechanism most of the time.

Assuming what mechanism will be used will most likely result in a poor user experience. Where appropriate, conform to the primary input mechanism, while only using secondary input mechanism capabilities as a last resort.

Examples

Let’s visit examples of how these features are used now to gain clarity.

With pointer, we have access to how accurate the pointing device’s input is. If the accuracy of this device is course (not accurate, such as a smartphone touchscreen), we can reflect the fact in element styling. We may wish to increase form element sizing to cater for the lack of accuracy:

/* default smaller styles for accurate pointer */input[type="checkbox"] {
min-width: 1rem;
min-height: 1rem;
}
/* enlarge checkboxes if pointer device is course */@media(pointer: course) {
input[type="checkbox"] {
min-width: 2rem;
min-height: 2rem;
}

We have defined default dimensions of checkboxes, followed by larger sizing for course pointing devices within the media query.

The same can be done for hover. For example, where there is no hovering functionality of the primary input device, we can opt to display scrollable menus instead of hidden dropdown menus upon hovering:

/* if hover-able, hide list by default, and hover to show */@media(hover) {
.dropdown
.list {
display: none;
max-height: 200px;
overflow-y: scroll;
position: absolute;
}
:hover {
.list {
display: block;
}
}
}
/* if not hover-able, show shorter scrollable list */ @media(hover: none) {
.dropdown .list {
display: inline-block;
max-height: 100px;
position: relative;
}
}

Note: Remember, these tests are for the primary input mechanism. Use any-hover and any-pointer to test all input mechanisms of a device.

This demonstrates that we can now wrap :hover styling within media queries to ensure that hovering only happens when the input mechanism is actually capable of doing so.

Relating to the problems we mentioned at the top of this article, we can now use the hover media feature to ensure elements can be hovered over using the primary input mechanism:

/* Only hover if primary input can do so */@media(hover) {
...
}

We can limit this further by combining pointer and hover in one media query, limiting hovering to accurate pointing devices:

/* Only hover if primary input is an accurate pointing device */@media(hover: hover) and (pointer: fine) {
...
}

Styles wrapped within this query will be triggered with mice & trackpads and accurate styluses, ensuring there will be no unpredictable or confusing UX behaviour.

In Summary

Although not a perfect solution for multi-input devices, Interaction Media Queries take the first step into probing what exactly is being used as a means of input, allowing developers to tailor components for that input mechanism’s capabilities.

This is a strong step towards better global device support for any given web app, although there is still a level of ambiguity with devices where multiple input mechanisms are presented. For example, tablets with styluses and touch screens present two very different input mechanisms, as do desktop computers with touch screens. In such cases, it is recommended to fall back to the primary input device.

It is interesting to point out that Apple devices do not have this issue, as they strictly stick to one input mechanism ideal. The Apple Pencil for example is not intended to be a stylus, leaving developers to solely focus on a hover: none and pointer: course setup. MacBooks have always only ever supported the trackpad and will not incorporate a touchscreen, making them ideal for the hover: hover and pointer: fine media query. Even the Apple TV adheres to a consistent UI where components are highlighted as you scroll through them.

For devices that try to be too much, perhaps it is a fair assumption that design compromises will always be made; hover and pointer being among those compromises.

What could be next?

Beyond the introduction of pointer and hover, it would be a somewhat natural evolution for the browser to recognise which input mechanism is being used at any given time. This additional feedback could determine which media queries would correlate to the current input mechanism being used, which would trigger style re-renders, just as window resizing does today with responsive applications.

Refer to the Media Queries Level 4 editor’s draft here for the complete specifications.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade