Designing for Different Devices (Viewport Size, Touch/Hover, etc.)

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

Viewport Size

The most important characteristic to consider is the viewport size. We design “mobile first” and then add customizations for larger devices as needed. The breakpoints we use are:

Breakpoint Size Pixels (at 16px body font size)
sm 40rem 640px
md 48rem 768px
lg 64rem 1024px
xl 80rem 1280px
2xl 96rem 1536px

To use these in an SCSS file, add @use "lib/viewport"; at the top of the file, then use one of the available mixins:

@use "lib/viewport";

@include viewport.from(lg) {
  // SCSS rules here will be applied to
  // devices larger than the lg breakpoint
}

@include viewport.until(sm) {
  // SCSS rules here will be applied to
  // devices smaller than the sm breakpoint
}

@include viewport.between(sm, md) {
  // SCSS rules here will be applied to
  // devices with a size between the sm
  // and md breakpoints
}

In general, SCSS is the recommended way to handle layout differences based on viewport size. For advanced cases, the same breakpoints can be accessed in Ember components via the capabilities service. For example:

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

class MyComponent extends Component {
  @service capabilities;

  <template>
    {{#if this.capabilities.viewport.lg}}
      This text will be displayed for devices larger than the lg breakpoint
    {{/if}}

    {{#unless this.capabilities.viewport.sm}}
      This text will be displayed for devices smaller than the sm breakpoint
    {{/unless}}
  </template>
}

These properties are reactive, and Ember will automatically re-render the relevant parts of the template as the browser is resized.

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:

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:

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 discussed above. We will be removing the dedicated modes in the near future, making “mobile mode” an alias for “viewport width less than sm” for backwards compatibility.


This document is version controlled - suggest changes on github.

11 „Gefällt mir“

Wäre dann etwas wie das hier veraltet?

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

Wenn Sie es in einem statischen Kontext tun, dann ja, das wird nicht mit dem bevorstehenden „viewportbasierten mobilen Modus“ (derzeit deaktiviert) kompatibel sein.

Wenn Sie die Prüfung in einem Autotracking-Kontext wie diesem durchführen:

@service site;
...

<template>
  {{#if this.site.mobileView}}
    ...
  {{/if}}
</template>

Dann rendert Ember die Dinge automatisch neu, wenn sich der mobileView-Boolesche Wert ändert (d. h. wenn der Browser in der Größe geändert wird). Das ist also in Ordnung.

Um sicherzugehen: Das Einsetzen in einen Getter ist veraltet, aber das Nicht-Einsetzen in <template> nicht?

Es ist auch in Ordnung, es in einen Getter zu setzen, da dies von Ember automatisch verfolgt wird.

@service site;

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


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

^^ das ist in Ordnung

Ein schlechtes Beispiel wäre

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

Da in dieser Situation mobileView nur beim Starten der Anwendung überprüft wird. Das Ändern der Größe des Browsers führt nicht dazu, dass der Initialisierer erneut ausgeführt wird.

Sie würden also etwas wie Folgendes umstrukturieren:

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

Damit Änderungen an mobileView wirksam werden, wenn die Größe des Browsers geändert wird.

3 „Gefällt mir“

Verstanden. Danke für die Erklärung!

1 „Gefällt mir“

(Beitrag vom Autor gelöscht)

1 „Gefällt mir“

Die allgemeine Empfehlung lautet: Tun Sie das nicht. Warum sollten sich solche Erfahrungen je nach Bildschirmgröße unterscheiden?

Ein nützliches Gedankenexperiment ist: Wie erwarten Sie, dass es sich auf faltbaren Telefonen oder Tablets verhält, die nicht explizit in die Kategorien Mobil/Desktop passen.

Wenn Sie diese Art von Verhaltensänderung wirklich auf der User-Agent-Zeichenfolge des Browsers basieren möchten (wie die alten Mobil-/Desktop-Modi funktionierten), dann haben wir capabilities.isMobileDevice, das buchstäblich nach dem Wort „mobile“ in der User-Agent-Zeichenfolge sucht:

1 „Gefällt mir“

Nun, in meinem Fall ist es so, dass ich die Option für Desktops biete, die Homepage zur Tag-Schnittstellenroute zu wechseln – deren Oberfläche auf Mobilgeräten nicht existiert … (obwohl die Route tatsächlich funktioniert – zusätzliche Steuerelemente sind ausgeblendet)

… aber der Punkt ist verstanden, ich werde das wahrscheinlich überdenken!

1 „Gefällt mir“

Interessant! Ich frage mich, ob das beabsichtigt ist… Ich finde, es sollte auf jedem Gerät funktionieren :thinking:

2 „Gefällt mir“

bedeutet das, dass Discourse pull-left veralten lassen wird? Denn mit dieser Klasse ist es irgendwie schrecklich zu arbeiten, und ich würde mich freuen, sie loszuwerden.

das wäre wirklich schön :wink:

Die Lösung steht in der Nachricht

Anstatt die Komponente bedingt einzubinden, binden Sie sie immer ein und lassen Sie die Komponente sich selbst bedingt rendern.

1 „Gefällt mir“

Ich verstehe, aber ich besitze diese Komponente nicht, sie ist Teil des Kerns und auf einer bestimmten Route – wenn der Kern dies für Mobilgeräte und Desktops funktionsfähig machen würde, wäre das die beste Lösung.

So wie es aussieht, können Sie diese Seite zur Startseite (global) machen, aber auf Mobilgeräten ist das sinnlos, da sie nicht funktioniert.

Ich gebe Ihnen ein weiteres Beispiel, bei dem dies derzeit nicht vollständig ausgenutzt wird und etwas knifflig ist – die Kategorieseite – auf dem Desktop so belassen, aber auf Mobilgeräten möglicherweise in eine „Neueste“-Seite umgewandelt werden, indem das Kategorie-Panel ausgeblendet und nur die Themenliste angezeigt wird – da meiner Meinung nach nur die Kategorieansicht und keine Themenliste (auf Mobilgeräten) schlecht ist.

Aber dann wäre dies auf Mobilgeräten keine „Kategorieseite“.

Dies wird nun dadurch verschärft, dass die Theme-Komponente der mobilen Startseite nicht mehr funktioniert, da das Gerät während der Initialisierung nicht mehr identifiziert werden kann.

Ich denke daher, dass die Discovery-Routen möglicherweise eine Art Überdenken hinsichtlich der Interoperabilität und Eignung für das vorherrschende Gerät benötigen.

2 „Gefällt mir“