A new way to test for touch devices without JavaScript: Enter the pointer media query
Tags: css, tutorial
Published: 2023-09-09 14:51:15

One of the age-old problems of responsive web-design is how do we distinguish between mobile and desktop.
Of course your gonna say that this is a trivial problem: We have media queries, dummy!

```css
@media (max-width: 768px) {
  /* Styling goes here for handheld, ie "mobile" devices */
}
```

```css
@media (min-width: 1200px) {
  /* Styling goes here for laptops and desktops */
}
```

So that's it we can call it a day and move on with our lives, right?
Not quite!

Let's start with the fact that the above code (and most other media queries we write on a day to day basis) carry with them a **lot** of assumptions.

We assume that it is an iPad or other similar tablet if the viewport is less the or equal to `768px` but maybe our user just squished the window of his browser on his 8K ultra-wide monitor so that he can put several windows next to each other.

We also assume that a device with a display that's at last `1200px` wide is some sort of laptop, however there are plenty of tablets that are more then `1200px` wide in landscape mode.

These assumptions are usually safe when we're just talking about layout and we try to find different breakpoints to tailor our design to different screen sizes.

But the problems start when we want different **behavior** for mobile devices. Like say we want to show a small dropdown calendar on desktop but on mobile we want a pop-up date picker component.

The gist of the problem is that sometimes we need different UI for the devices where the primary method of input is touch and the ones where it's a mouse.

Until recently we had to result to various JS hacks to detect touch capabilities of a device. For example here's one popular snippet:

```js
function isTouchDevice() {
  return (
    "ontouchstart" in window ||
    navigator.maxTouchPoints > 0 ||
    navigator.msMaxTouchPoints > 0
  );
}
```

There's two problems with this approach

1. They are flaky and unreliable. We can only test for the presence of [touch events](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events) implemented by the browser but that does not necessarily imply that the device has a touch screen.
2. You need client-side JS for this to run, if you make the interactions depend on some JS to run that might result in a bad experience when the JS has not yet downloaded. You would want these sort of tests to run immediately as soon as the user can interact with the page

However now we have an actual media query to test what pointer type the device supports.

The syntax looks like this

```css
@media (pointer: none|coarse|fine) {
  /* Some code based on the supported pointer value */
}
```

The value is an enum with three possible values as defined in the [spec](https://drafts.csswg.org/mediaqueries/#pointer).

> `none`
>
> The primary input mechanism does not include a pointing device.
>
> `coarse`
>
> The primary input mechanism includes a pointing device of limited accuracy, such as a finger on a touchscreen.
>
> `fine`
>
> The primary input mechanism includes an accurate pointing device, such as a mouse.

Using this query we can for example make checkboxes and radio buttons larger for devices that have a `coarse` pointer.

{% codepen https://codepen.io/cooty/pen/abPJBMz %}

Of course we can also use this query from JS to manipulate the behavior of our UI or load entirely different components optimized for touch versus mouse inputs.

{% codepen https://codepen.io/cooty/pen/OJrbbBo %}

If you were wondering about "Can I already use this in production, how is this thing supported?" I have good news for you: The [caniuse](https://caniuse.com/css-media-interaction) table for the feature is quite nice and green!

Also [MUI X](https://mui.com/x/) UI library uses this method in their [date picker](https://mui.com/x/react-date-pickers/date-picker/#available-components) component.

Hope you can utilize this new feature of the web platform to make your UI code more elegant and our users happier!