# Designing for Different Devices (Touch & Hover)

**URL:** https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810
**Category:** Developer Guides
**Created:** [May 27, 2025, 6:17pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810 "2025-05-27T18:17:05Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![system](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/system/32/443519_2.png) [@system](https://meta.discourse.org/u/system)
#### Post date: [May 27, 2025, 6:17pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/1 "2025-05-27T18:17:05Z")

</div>

This document outlines the APIs used to adapt Discourse’s user interface for different devices.

### Touch & Hover

Some devices only have touchscreens, some only have a traditional mouse pointer, and some have both. Importantly, touchscreen users cannot “hover” over elements. Therefore, interfaces should be designed to work entirely without hover states, with hover-specific enhancements added for devices that support them.

There are several ways to detect touch/hover capability via CSS and JavaScript. For consistency, we recommend using Discourse’s helpers instead of those CSS/JS APIs directly.

For CSS, you can target the `.discourse-touch` and `.discourse-no-touch` classes, which are added to the `<html>` element. These are determined based on the `(any-pointer: coarse)` media query.

For example:

```scss
html.discourse-touch {
  // SCSS rules here will apply to devices with a touch screen,
  // including mobiles/tablets and laptops/desktops with touch screens.
}

html.discourse-no-touch {
  // SCSS rules here will apply to devices with no touch screen.
}

```

This information is also available in Ember components via the capabilities service:

```gjs
import Component from "@glimmer/component";
import { service } from "@ember/service";

class MyComponent extends Component {
  @service capabilities;

  <template>
    {{#if this.capabilities.touch}}
      This text will be displayed for devices with a touch screen
    {{/if}}

    {{#unless this.capabilities.touch}}
      This text will be displayed for devices with no touch screen
    {{/unless}}
  </template>
}

```

### Legacy Mobile / Desktop Modes

Historically, Discourse shipped two completely different layouts and stylesheets for “mobile” and “desktop” views, based on the browser’s user-agent. Developers would target these modes by putting CSS in specific mobile/desktop directories, by using the `.mobile-view`/`.desktop-view` HTML classes, and the `site.mobileView` boolean in JavaScript.

These techniques are now considered deprecated and should be replaced with the [viewport and capability-based strategies](https://meta.discourse.org/t/-/409279) discussed in the next document. For backwards-compatibility, legacy desktop/mobile CSS is used when the viewport is larger/smaller than the `sm` threshold.

## See also

- [Designing for Responsive Widths](https://meta.discourse.org/t/-/409279) — breakpoints, viewport size, and container queries.
- [Guidelines for CSS classes using BEM](https://meta.discourse.org/t/-/361851) — CSS class naming conventions.

* * *

This document is version controlled - suggest changes [on github](https://github.com/discourse/discourse/blob/main/docs/developer-guides/docs/03-code-internals/28-designing-for-devices.md).

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [August 29, 2025, 10:21am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/2 "2025-08-29T10:21:19Z")

</div>

So something like this would be deprecated?

```js
@service site;
...
const mobileView = this.site.mobileView;

```

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [August 29, 2025, 10:27am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/3 "2025-08-29T10:27:32Z")

</div>

If you do it in a static context like an initializer, then yes, that’s not going to be compatible with the upcoming “viewport based mobile mode” (currently disabled by default, but will be enabled soon).

If you do the check in an autotracking context like this:

```gjs
@service site;
...
<template>
  {{#if this.site.mobileView}}
    ...
  {{/if}}
</template>

```

Then Ember will automatically re-render things when the mobileView boolean changes (i.e. when the browser is resized). So that’s fine.

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [August 29, 2025, 10:29am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/4 "2025-08-29T10:29:18Z")

</div>

So just to be sure, putting it in a getter is deprecated, but not putting it in `<template>`?

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [August 29, 2025, 10:33am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/5 "2025-08-29T10:33:41Z")

</div>

Putting it in a getter is also fine, since that’ll be autotracked by Ember.

```gjs
@service site;

get shouldRender(){
  return this.site.mobileView;
}

<template>
  {{#if this.shouldRender}}
    ...
  {{/if}}
</template>

```

^^ this is fine

* * *

A bad example would be

```gjs
export default apiInitializer((api) => {
  const site = api.container.lookup("service:site");
  if(site.mobileView){
    api.renderInOutlet("some-outlet", <template>My content</template>)
  }
});

```

Because in this situation, `mobileView` is only checked when the application boots. Resizing the browser won’t re-run the initializer.

So you would refactor to something like

```gjs
export default apiInitializer((api) => {
  const site = api.container.lookup("service:site");
  api.renderInOutlet("some-outlet", <template>
    {{#if site.mobileView}}My content{{/if}}
  </template>);
});

```

That way: changes to `mobileView` will take effect when the browser is resized.

* * *

An easy way to test all this: enable “viewport based mobile mode” site setting, then try resizing your browser and check that the layout updates correctly when you go between narrow/wide browser widths.

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [August 29, 2025, 10:36am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/6 "2025-08-29T10:36:28Z")

</div>

Understood now. Thanks for the explanation!

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [October 17, 2025, 10:55am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/7 "2025-10-17T10:55:03Z")

</div>

“Deprecation notice: Accessing `capabilities.viewport.sm` during the site initialization phase is not recommended. Using these values during initialization can lead to errors and inconsistencies when the browser window is resized. Please move these checks to a component, transformer, or API callback that executes during page rendering. [deprecation id: discourse.static-viewport-initialization]”

I’ve been using initialisers to switch on and off various functionality based on whether we are in a mobile view or not (e.g. changing the default Homepage, adding a community section link for example). These aren’t really meant to be dynamic and reactive - suggestions welcome on how to treat these use cases and make sure I can defeat this notice.

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [October 17, 2025, 12:56pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/8 "2025-10-17T12:56:29Z")

</div>

The general recommendation is: don’t do that. Why should those kind of experiences differ based on the size of the screen?

A useful thought experiment is: how do you expect it to behave on folding phones or tablets, which don’t explicitly fit into the mobile/desktop buckets.

If you really do want this kind of behaviour change to be based on the user-agent of the browser (as the old mobile/desktop modes worked), then we have `capabilities.isMobileDevice`, which quite literally checks for the word “mobile” in the user-agent string:

> <https://github.com/discourse/discourse/blob/e65395cce490c9a7ecd974ca9c8803a38f26c3cc/app/assets/javascripts/discourse/app/services/capabilities.js#L105C3-L110C4>

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [October 17, 2025, 1:08pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/9 "2025-10-17T13:08:03Z")

</div>

> [@david](#):
>
> Why should those kind of experiences differ based on the size of the screen?

well in my case it’s that I provide the option for desktop to switch homepage to the tag intersections route - whose interface doesn’t exist on mobile … (though the route actually works - additional controls are hidden)

… but point taken, I’ll probably rethink this!

---

<div class="post-metadata">

### Author: ![david](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/david/32/157490_2.png) [@david](https://meta.discourse.org/u/david)
#### Post date: [October 17, 2025, 1:22pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/10 "2025-10-17T13:22:08Z")

</div>

> [@merefield](#):
>
> the tag intersections route - whose interface doesn’t exist on mobile

Interesting! I wonder if that’s deliberate… I feel like it should work on any device 🤔

---

<div class="post-metadata">

### Author: ![Lilly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lilly/32/575047_2.png) [@Lilly](https://meta.discourse.org/u/Lilly)
#### Post date: [January 25, 2026, 3:45am UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/11 "2026-01-25T03:45:17Z")

</div>

so does this mean Discourse is going to deprecate `pull-left`? because that class is kind of horrible to work with and i’d love to see it gone.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [January 25, 2026, 8:50pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/12 "2026-01-25T20:50:30Z")

</div>

> [@david](#):
>
> Interesting! I wonder if that’s deliberate… I feel like it should work on any device 🤔

that would be really nice 😉

---

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [January 25, 2026, 9:22pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/13 "2026-01-25T21:22:56Z")

</div>

> [@merefield](#):
>
> - suggestions welcome on how to treat these use cases and make sure I can defeat this notice.

The solution is in the message

> [@](#):
>
> Please move these checks to a component, transformer, or API callback that executes during page rendering

Instead of conditionally including the component, always include it, and have the component conditionally render itself.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [January 25, 2026, 9:24pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/14 "2026-01-25T21:24:49Z")

</div>

I hear you, but I don’t own this component, it’s part of core and on a specific route - if core were to make this work on mobile and desktop that would be the best solution.

As it stands you can make this page the homepage (globally) but on mobile that is pointless as it doesn’t work.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [January 25, 2026, 9:34pm UTC](https://meta.discourse.org/t/designing-for-different-devices-touch-hover/367810/15 "2026-01-25T21:34:26Z")

</div>

I’ll give you another example where this is currently not fully exploited and a little tricky - Categories page - on desktop leave as is but maybe turn it into a Latest page on mobile by hiding the Categories panel and just leave the Topic List - since only having the Categories view and no Topic List (on mobile) imho sucks (as a home page).

But then on mobile this wouldn’t be a “Categories” page anymore (despite the route name) 🤔

This is now exacerbated by the fact that the [force mobile home page](https://meta.discourse.org/t/force-mobile-homepage/95705/29) theme component doesn’t work any more because you can’t identify the device during init.

I think therefore that the discovery routes may need some kind of rethink for interoperability and suitability for the prevailing device.
