Designing for Different Devices (Touch & Hover)

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:

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 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


This document is version controlled - suggest changes on github.

13 curtidas

Então algo assim seria descontinuado?

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

Se você fizer isso em um contexto estático, então sim, isso não será compatível com o próximo “modo móvel baseado em viewport” (atualmente desabilitado).

Se você fizer a verificação em um contexto de autotracking como este:

@service site;
...

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

Então o Ember irá automaticamente renderizar novamente as coisas quando o booleano mobileView mudar (ou seja, quando o navegador for redimensionado). Então, tudo bem.

Então, só para ter certeza, colocá-lo em um getter é obsoleto, mas não colocá-lo em <template>?

Colocá-lo em um getter também está bom, pois será rastreado automaticamente pelo Ember.

@service site;

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


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

^^ isso está bom

Um mau exemplo seria

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

Porque nesta situação, mobileView é verificado apenas quando a aplicação é iniciada. Redimensionar o navegador não executará o inicializador novamente.

Então você refatoraria para algo como

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

Assim, as alterações em mobileView terão efeito quando o navegador for redimensionado.

3 curtidas

Entendido agora. Obrigado pela explicação!

1 curtida

(postagem excluída pelo autor)

1 curtida

A recomendação geral é: não faça isso. Por que esse tipo de experiência deveria ser diferente com base no tamanho da tela?

Um experimento mental útil é: como você espera que ele se comporte em telefones dobráveis ou tablets, que não se encaixam explicitamente nos baldes de celular/desktop.

Se você realmente quiser que esse tipo de mudança de comportamento seja baseado no user-agent do navegador (como funcionavam os antigos modos mobile/desktop), então temos capabilities.isMobileDevice, que literalmente verifica a palavra “mobile” na string do user-agent:

1 curtida

bem, no meu caso, é que eu forneço a opção para desktop de alternar a página inicial para a rota de interseções de tags - cuja interface não existe no celular… (embora a rota realmente funcione - controles adicionais estão ocultos)

… mas o ponto é válido, provavelmente vou repensar isso!

1 curtida

Interessante! Fico imaginando se isso é deliberado… Sinto que deveria funcionar em qualquer dispositivo :thinking:

2 curtidas

então isso significa que o Discourse vai descontinuar o pull-left? porque essa classe é meio horrível de se trabalhar e eu adoraria vê-la sumir.

isso seria muito bom :wink:

A solução está na mensagem

Em vez de incluir o componente condicionalmente, inclua-o sempre e faça com que o componente se renderize condicionalmente.

1 curtida

Eu entendo, mas eu não sou o proprietário deste componente, ele faz parte do núcleo e está em uma rota específica - se o núcleo fizesse isso funcionar no celular e no desktop, essa seria a melhor solução.

No estado atual, você pode tornar esta página a página inicial (globalmente), mas no celular isso é inútil, pois não funciona.

Vou dar outro exemplo onde isso atualmente não é totalmente explorado e é um pouco complicado - Página de Categorias - no desktop, deixe como está, mas talvez transforme-a em uma página de Últimas no celular, ocultando o painel de Categorias e deixando apenas a Lista de Tópicos - já que ter apenas a visualização de Categorias e nenhuma Lista de Tópicos (no celular) na minha opinião é péssimo.

Mas então, no celular, esta não seria uma página de “Categorias”.

Isso agora é exacerbado pelo fato de que o componente de tema da página inicial móvel não funciona mais porque você não consegue identificar o dispositivo durante a inicialização.

Portanto, acho que as rotas de descoberta podem precisar de algum tipo de reformulação para interoperabilidade e adequação ao dispositivo predominante.

2 curtidas