Modal de Pré-visualização do Tópico

Instale este componente de tema

Modal de Pré-visualização de Tópico – abra e interaja com tópicos sem sair da lista de tópicos

Criei um novo componente de tema do Discourse chamado Modal de Pré-visualização de Tópico.

A ideia é bastante simples:

Abrir um tópico diretamente da lista de tópicos em um modal nativo do Discourse, ler e interagir com o tópico e, em seguida, continuar navegando na lista sem precisar sair dela.

Tudo começou em Facebook-style Topic Modal - Is it better? , mas acabou exigindo uma integração bastante complexa com os sistemas de tópicos, fluxo de posts, compositor, modal, favoritos, roteamento, presença, rastreamento de leitura e pré-busca do Discourse.


Por quê?

O fluxo normal do Discourse é:

  1. Você está navegando em uma lista de tópicos.
  2. Você clica em um tópico.
  3. O Discourse navega para /t/....
  4. Você lê/responde/interage com o tópico.
  5. Você volta para a lista de tópicos.

Para muitos fluxos de trabalho, isso é perfeitamente adequado.

No entanto, ao navegar em uma lista de tópicos movimentada, às vezes eu só quero inspecionar rapidamente um tópico, ler alguns posts, verificar as últimas respostas, reagir a algo ou responder a uma pergunta rápida.

Para esse caso de uso, sair da lista de tópicos parece desnecessariamente custoso.

O objetivo deste componente foi, portanto, fazer com que a lista de tópicos se comportasse mais como uma caixa de entrada:

lista de tópicos → pré-visualização → interação → fechar → continuar exatamente de onde parou.


O que ele faz

A pré-visualização não é apenas um trecho estático.

Ela renderiza os componentes de post reais do Discourse dentro de um DModal nativo.

Isso significa que os usuários podem:

  • ler posts
  • rolar pelo tópico
  • carregar posts anteriores
  • carregar mais posts abaixo
  • reagir a posts
  • favoritar posts
  • citar texto
  • responder ao tópico
  • responder a posts individuais
  • editar posts quando permitido
  • excluir/recuperar posts quando permitido
  • sinalizar posts
  • ver o histórico do post
  • executar várias ações normais de post
  • ver a presença no tópico
  • seguir links para outros posts dentro do mesmo tópico
  • pular diretamente para o post relevante
  • abrir o tópico completo quando necessário

A intenção é que a pré-visualização se sinta o mais próximo possível de abrir o tópico de verdade.


Dois modos de gatilho

Há duas maneiras de abrir a pré-visualização.

1. Linha inteira da lista de tópicos

Este é o padrão.

A linha inteira da lista de tópicos se torna clicável, enquanto elementos interativos comuns como:

  • cartões de usuário
  • participantes
  • links de categoria
  • tags
  • links de status do tópico
  • seleção em massa

são excluídos do gatilho do modal.

Isso torna a experiência muito rápida ao navegar em uma lista de tópicos.

2. Botão de expansão explícito

Como alternativa, o componente pode renderizar um pequeno ícone de expansão por meio de uma saída de plugin do Discourse. Temas personalizados podem simplesmente criar uma nova <PluginOutlet /> para mostrar o gatilho.

Neste modo, o comportamento normal da lista de tópicos permanece completamente intacto.

O usuário clica no ícone de expansão para abrir a pré-visualização, enquanto clicar no título do tópico ainda executa a navegação normal do Discourse.

Isso é útil se um site quiser preservar o modelo de interação padrão da lista de tópicos.

A configuração é:

trigger_style:
  row

ou:

trigger_style:
  button

Ao usar o modo de botão, a saída também é configurável.


A pré-visualização começa na posição não lida do usuário

Um dos detalhes importantes é que o modal não carrega simplesmente o primeiro post.

Quando um tópico já foi parcialmente lido, a pré-visualização calcula:

last_read_post_number + 1

e abre em torno desse post.

Então, se um tópico tem 200 posts e o usuário leu até o post #165, abrir a pré-visualização começa em torno do #166.

Isso torna a pré-visualização muito mais útil para a navegação do mundo real.

Isso também significa que o componente tem que lidar com ambos os lados do fluxo de posts:

  • carregar posts anteriores quando necessário
  • carregar posts mais recentes abaixo

O botão Posts anteriores é exibido quando há posts acima do intervalo atualmente carregado, enquanto um sentinel IntersectionObserver carrega automaticamente mais posts quando o usuário chega ao final.


Pré-busca

Uma das maiores partes do componente é seu sistema de pré-busca.

O problema com um modal como este é que o usuário espera que ele seja instantâneo.

Se começarmos a carregar o tópico apenas depois que o usuário clicar, o modal ainda pode gastar tempo perceptível esperando pela rede.

Em vez disso, o componente pode fazer pré-busca proativa de tópicos enquanto o usuário navega na lista.

Quando uma linha de tópico se aproxima da viewport, um IntersectionObserver pode agendar uma pré-busca.

Há várias salvaguardas para evitar que isso se transforme em tráfego de fundo descontrolado.

Debouncing

Um tópico não aciona imediatamente uma solicitação apenas porque apareceu brevemente na viewport.

O componente espera pelo período de debounce configurado.

Padrão:

400 ms

Isso é particularmente útil ao rolar rapidamente por uma lista de tópicos longa.

Margem raiz

A pré-busca pode começar um pouco antes que o tópico entre realmente na viewport.

Padrão:

50 px

Isso dá à solicitação uma pequena vantagem.

Limite de solicitações simultâneas

O número de pré-buscas simultâneas é limitado.

Padrão:

2

A configuração permite entre 1 e 6 pré-buscas simultâneas.

Orçamento por minuto

Há também um segundo mecanismo de proteção:

max_prefetches_per_minute

O padrão é:

15

Então, mesmo que o usuário continue rolando por centenas de tópicos, o componente não gerará continuamente solicitações especulativas.

0 desativa o limite.

A pré-busca pode ser desativada completamente

Se um site não quiser nenhum tráfego de rede especulativo:

enable_prefetch = false

O componente continua funcionando normalmente. Os tópicos simplesmente são carregados quando a pré-visualização é aberta.


Os dados de pré-busca são mantidos separados da navegação normal de tópicos

Há um detalhe de implementação importante aqui.

A resposta pré-buscada não é gravada imediatamente na chave de pré-carregamento normal topic_<id> do Discourse.

Em vez disso, o componente usa seu próprio namespace:

topic-preview-modal:prefetch:<topicId>

Somente quando o usuário realmente abre a pré-visualização é que a promessa pré-buscada é promovida para a chave de pré-carregamento do tópico principal.

Isso é intencional.

A pré-visualização pode estar carregando um tópico a partir de last_read_post_number + 1, e eu não quero que essa resposta específica da pré-visualização vaze para uma navegação de rota de tópico normal.

Então, o ciclo de vida é essencialmente:

tópico entra na viewport
        ↓
pré-busca
        ↓
armazenamento de pré-carregamento privado
        ↓
usário abre pré-visualização
        ↓
promover pré-carregamento
        ↓
Topic.find()/PostStream usa a mesma promessa

Isso também significa que o modal não precisa esperar que a solicitação de pré-busca seja concluída antes de abrir.

O modal pode abrir imediatamente com seu esqueleto enquanto a mesma promessa continua sendo resolvida.


Suporte a dispositivos móveis

Na verdade, esta foi uma das razões pelas quais passei consideravelmente mais tempo na implementação.

A ideia inicial funcionava razoavelmente bem na área de trabalho, mas os dispositivos móveis expuseram vários problemas relacionados a:

  • interação por toque
  • rolagem do modal
  • foco
  • menus aninhados
  • o compositor
  • visibilidade do post
  • carregamento de imagens
  • desempenho

A implementação final, portanto, evita tratar o modal como um fórum miniatura completamente separado.

Em vez disso, reutiliza o máximo possível da infraestrutura existente do Discourse.


Componentes de post reais do Discourse

O modal não recria posts usando um modelo personalizado simplificado.

Ele renderiza os componentes reais do Discourse:

Post
PostSmallAction

Isso é importante porque, caso contrário, a pré-visualização se tornaria rapidamente uma segunda implementação da interface do usuário do post.

O componente passa as ações relevantes para os componentes de post normais, incluindo coisas como:

  • responder
  • editar
  • excluir
  • recuperar
  • sinalizar
  • histórico
  • favorito
  • wiki
  • bloquear/desbloquear
  • tipo de post
  • mudanças de propriedade
  • badges
  • posts ocultos
  • citação
  • etc.

O resultado é que a pré-visualização pode se comportar muito mais como um tópico normal do que um componente de “pré-visualização” tradicional.


Respostas e o compositor

O compositor é uma das partes mais complicadas.

A pré-visualização pode abrir o compositor normal do Discourse para:

Responder ao tópico

O compositor do tópico é aberto com o modelo do tópico e as informações de rascunho corretas.

Responder a um post específico

O post é passado para o compositor para que a resposta se comporte como uma resposta normal a um post.

Citar texto selecionado

O componente também se integra com PostTextSelection.

Isso significa que os usuários podem selecionar texto dentro da pré-visualização e usar o fluxo normal de citação/resposta do Discourse.


Modais aninhados

Outra parte complicada foi o sistema de modais do Discourse.

Os posts podem abrir outros modais e diálogos:

  • sinalização
  • histórico
  • diálogos relacionados a badges
  • mudanças de propriedade
  • confirmações de exclusão
  • etc.

Se esses fossem permitidos para interagir com o serviço global de modais normalmente, abrir um deles poderia fechar toda a pré-visualização do tópico.

Para evitar isso, o componente cria um mecanismo de sub-modal local.

Conceitualmente:

Modal de Pré-visualização de Tópico
        │
        ├── Modal de sinalização
        ├── Modal de histórico
        ├── Confirmação de exclusão
        ├── Modal de badge
        └── outro modal relacionado a posts

A pré-visualização permanece montada abaixo.

O componente corrige temporariamente os métodos relevantes do serviço de modal enquanto está ativo e os restaura quando é destruído.


Roteamento dentro do modal

Outro detalhe importante são os links para posts dentro do mesmo tópico.

Por exemplo, se um post contém um link para:

/t/meu-topico/123

a pré-visualização não precisa fechar e navegar para fora.

Em vez disso, o componente intercepta a navegação do mesmo tópico e pula para o post solicitado dentro do modal.

O mesmo se aplica a links que apontam para o tópico sem um número de post específico.

Isso mantém o usuário dentro da pré-visualização.

Se o link apontar para um tópico genuinamente diferente, o componente primeiro restaura suas correções temporárias de serviço e se fecha antes de permitir a transição de rota normal do Discourse.

Essa limpeza é importante porque, caso contrário, as assinaturas e o rastreador de tempo da pré-visualização poderiam permanecer ativos enquanto a rota real do tópico está sendo inicializada.


Rastreamento de leitura e rastreamento de tempo

Eu também queria que a pré-visualização se comportasse corretamente do ponto de vista do Discourse.

Abrir uma pré-visualização não deve significar que o rastreamento de leitura é completamente ignorado.

O componente, portanto, lida com:

  • rastreamento de visita ao tópico
  • rastreamento de posts visíveis
  • tempo do tópico
  • atualizações do último post lido

O rastreador de tempo usa um IntersectionObserver para determinar quais posts estão realmente visíveis.

A cada 5 segundos, o tempo de posts visíveis é enviado para:

/topics/timings

Quando o modal é fechado, é executado um envio final para que os últimos segundos não sejam perdidos.

A implementação também limita um único intervalo de tempo a 60 segundos.


Mantendo o estado não lido da lista de tópicos sincronizado

Havia outro problema sutil aqui.

Atualizar apenas o estado de rastreamento de tópicos do Discourse não é suficiente para atualizar o emblema não lido exibido diretamente em uma linha da lista de tópicos.

O componente, portanto, atualiza o objeto de tópico real associado à linha após as informações de tempo terem sido enviadas.

Ele atualiza valores como:

last_read_post_number
unread_posts
unread
new_posts

quando apropriado.

Isso significa que, após ler um tópico dentro do modal, a lista de tópicos pode refletir imediatamente o novo estado de leitura em vez de exigir uma atualização completa da página.


Visibilidade do post

A pré-visualização usa um IntersectionObserver compartilhado para determinar quando posts individuais se tornam visíveis.

Há também uma verificação de visibilidade síncrona quando o observador é anexado.

Isso lida com um caso de borda em que um post já está visível quando é montado, mas a primeira chamada de retorno assíncrona do IntersectionObserver ainda não foi acionada.

Isso é especialmente relevante para tópicos muito curtos onde todo o tópico já pode estar visível quando o modal é aberto.


Considerações de desempenho

Um objetivo principal era evitar transformar o modal em uma página de tópico miniatura pesada em termos de desempenho.

Algumas coisas são feitas especificamente para isso.

Renderização progressiva

O carregamento inicial não renderiza imediatamente todos os posts.

O componente primeiro renderiza posts suficientes para alcançar a posição de destino.

Os posts restantes são então renderizados progressivamente usando:

requestIdleCallback

quando disponível, com fallback para setTimeout.

Isso é particularmente útil ao abrir um tópico longo em torno de um post muito abaixo do fluxo.

Contenção CSS

Os posts usam:

contain: layout;
content-visibility: auto;
contain-intrinsic-size: 1px 180px;

Isso permite que o navegador evite fazer trabalho de renderização desnecessário para posts que não estão atualmente visíveis.

Imagens preguiçosas

Imagens que ainda não especificaram um modo de carregamento recebem automaticamente:

loading="lazy"
decoding="async"

Isso impede que um tópico longo com muitas imagens carregue tudo imediatamente.


Estado de carregamento

O modal não mostra apenas uma área branca/vazia enquanto a solicitação está sendo feita.

Ele tem uma interface de usuário de esqueleto com:

  • espaços reservados de avatar
  • espaços reservados de nome de usuário/nome
  • espaços reservados do corpo do post
  • animação de brilho

O brilho respeita:

prefers-reduced-motion

então a animação é desativada para usuários que solicitaram movimento reduzido.


Mantendo a posição de rolagem estável

Há alguns lugares onde o componente precisa manipular a posição de rolagem manualmente.

Por exemplo, ao carregar posts anteriores, o conteúdo recém-inserido aumenta a altura de rolagem.

Simplesmente adicionar os posts no início faria a posição atual do usuário pular.

O componente, portanto, registra a altura de rolagem anterior e compensa a diferença após a inserção dos posts.

Isso mantém o conteúdo atualmente visível aproximadamente no mesmo lugar.

O mesmo se aplica ao pular para um post específico.

O componente executa uma etapa de posicionamento pós-renderização e verifica a posição novamente em quadros subsequentes para levar em conta o conteúdo que ainda pode estar se estabilizando.


Presença no tópico

Quando os dados relevantes do tópico estão disponíveis, a pré-visualização também pode exibir as informações de presença do tópico do Discourse na parte inferior do modal.

Então, os usuários podem ver quem mais está visualizando o tópico atualmente sem precisar sair da pré-visualização.


Interação com menus móveis e foco

Os dispositivos móveis introduziram outra categoria de problemas.

Alguns elementos de interface do usuário do Discourse usam serviços de modal/menu compartilhados, e esses serviços não necessariamente sabem que a pré-visualização do tópico está atuando atualmente como um contexto de navegação aninhado.

O componente, portanto, possui tratamento adicional em torno de:

  • modal.close()
  • menus Float Kit
  • restauração de foco
  • o compositor
  • controles de teclado de lightbox
  • bloqueios de rolagem do corpo

Por exemplo, se um menu tentar internamente chamar o método de fechamento global do modal, isso não deve acidentalmente fechar toda a pré-visualização do tópico.

Da mesma forma, quando o compositor está aberto, o foco precisa permanecer dentro do compositor em vez de ser puxado de volta para o contexto de foco da pré-visualização.


Configuração

O componente atualmente expõe as seguintes configurações:

Configuração Padrão Descrição
trigger_style row Torna a linha inteira clicável ou usa um botão explícito
plugin_outlet topic-list-after-title Saída usada pelo gatilho de botão
enable_prefetch true Ativar/desativar pré-busca de tópicos em segundo plano
max_concurrent_prefetches 2 Número máximo de solicitações de pré-busca simultâneas
prefetch_debounce_ms 400 Atraso antes de iniciar uma pré-busca
prefetch_root_margin_px 50 Iniciar pré-busca este número de pixels antes que a linha entre na viewport
max_prefetches_per_minute 15 Número máximo de solicitações especulativas por minuto

Os controles de pré-busca são intencionalmente configuráveis porque comunidades diferentes podem ter padrões de tráfego e características de hospedagem/rede muito diferentes.


Um dos principais objetivos de design: não quebrar o Discourse normal

Tentei manter o componente o mais próximo possível da arquitetura existente do Discourse.

Ele não implementa seu próprio renderizador de posts, seu próprio compositor, seu próprio modelo de tópico ou seu próprio fluxo de posts completamente separado.

Em vez disso, ele constrói um contexto de navegação temporário em torno dos componentes e serviços existentes do Discourse.

É também por isso que algumas partes da implementação são mais complicadas do que podem parecer à primeira vista.

O desafio mais interessante foi:

Um tópico pode se comportar quase como um tópico normal do Discourse enquanto é exibido dentro de outro contexto de interface do usuário?

Isso exigiu lidar com as fronteiras entre os serviços globais do Discourse e a pré-visualização local.

15 curtidas

Para sua informação:

Ele tem problemas com matemática. Mas isso pode ser outro caso extremo, embora.

1 curtida

Absoluto lendário :slight_smile: agora preciso descobrir como fazer isso funcionar com minha configuração :slight_smile: @awesomerobot de que depende o seu tema para o clique na linha inteira

api.renderInOutlet("topic-list-before-link", TopicListItemClick);
2 curtidas

Para quem usa o tema Reddit-ish, aqui está uma correção que funcionou para mim.

Compatibilidade com o tema Reddit-ish

Apenas um aviso para quem usa o tema Reddit-ish: o botão do modal funciona, mas o gatilho de linha padrão não.

O problema é que o Reddit-ish substitui o comportamento padrão da linha da lista de tópicos e trata cliques em todo o cartão do tópico. Por isso, o tratamento normal de clique na linha do modal não funciona como esperado.

Alterar a configuração do Modal de Pré-visualização do Tópico para:

Estilo de gatilho: botão
Saída do plugin: topic-list-after-title

funciona corretamente, porque o Reddit-ish já inclui a saída topic-list-after-title.

Para manter o comportamento de clique em todo o cartão, deixei o Modal de Pré-visualização do Tópico no modo botão e alterei a ação openTopic() existente do Reddit-ish para que ela acione o botão funcional do modal.

A ação original do Reddit-ish é:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
  } else {
    navigateToTopic(topic, topic.lastUnreadUrl);
  }
}

Eu a alterei para:

@action
openTopic(event) {
  if (
    (event.target.nodeName === "A" && !event.target.closest(".raw-link")) ||
    event.target.closest(".badge-wrapper") ||
    event.target.closest(".topic-preview-modal__trigger-wrapper")
  ) {
    return;
  }

  const { navigateToTopic, topic } = this.args.outletArgs;

  if (wantsNewWindow(event)) {
    window.open(topic.lastUnreadUrl, "_blank");
    return;
  }

  const previewButton = event.currentTarget.querySelector(
    ".topic-preview-modal__trigger-wrapper--button"
  );

  if (previewButton) {
    event.preventDefault();
    event.stopPropagation();
    previewButton.click();
    return;
  }

  navigateToTopic(topic, topic.lastUnreadUrl);
}

O gatilho do botão do modal é renderizado como:

<div class="topic-preview-modal__trigger-wrapper">
  <span
    role="button"
    class="topic-preview-modal__trigger-wrapper--button"
  >

Portanto, isso não recria nenhuma lógica do modal. Ele simplesmente faz com que o clique no cartão do Reddit-ish acione o botão de pré-visualização existente e funcional.

O resultado é:

  • O clique no cartão do tópico abre o modal de pré-visualização.

  • O clique no título do tópico abre o modal de pré-visualização.

  • O botão de pré-visualização continua funcionando.

  • O clique com Cmd/Ctrl ainda abre o tópico normal em uma nova aba.

  • A categoria e outros links normais continuam se comportando normalmente.

  • Se o botão de pré-visualização não estiver presente, o Reddit-ish volta à navegação de tópicos normal.

Portanto, o modal subjacente funciona bem com o Reddit-ish; a incompatibilidade é especificamente com o gatilho de linha padrão.

Eu também escondi o botão usando

.topic-preview-modal__trigger-wrapper {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}
3 curtidas

Agora, vamos tentar fazer as respostas aninhadas funcionarem no modal :slight_smile:

1 curtida

Você, meu amigo, é incrível. Este deve ser o componente de tema mais fantástico que vi há muito tempo.

A única coisa que sinto falta é uma largura maior / configurável, seria bom se pudéssemos torná-lo da mesma largura que as publicações na visualização do tópico.

2 curtidas

Ajustei a largura máxima para 800px

    .d-modal {
        --modal-max-width: 600px;
        --modal-width: 30em;
        --modal-min-width: 400px;
    }
1 curtida

Isso se aplicaria a todos os modais…
Fiz isso também para que o ícone se destacasse menos

.topic-preview-modal__trigger-wrapper--button .d-icon {
    fill: #888;
}

@media (width >= 40rem) {
  .d-modal.topic-preview-modal {
      --modal-max-width: 800px;
  }
}
2 curtidas

@Don Estou curioso: por que você não reutilizou o componente <PostList> do core?

1 curtida

Olá @Jagster, obrigado pelo relatório! Aqui está a correção: FIX: Replace visibility:hidden to allow MathJax rendering · VaperinaDEV/discourse-topic-preview-modal@f9eb70f · GitHub


Obrigado @RGJ, adicionei uma configuração para isso.


Essa é uma boa pergunta.

PostList/PostListItem foi projetado para listas de resumo baseadas em trechos (rascunhos, favoritos, feeds de atividade), não para renderizar o fluxo de posts interativo real de um tópico.

Ele renderiza @post.excerpt/expandedExcerpt através de DDecoratedHtml, além de um cabeçalho com avatar/título e um botão para expandir para o trecho. Não há ações de curtir, citar, responder, editar ou denunciar — nenhuma das funcionalidades interativas de post que o modal precisa.

A paginação do PostList também é unidirecional (fetchMorePosts apenas adiciona posts seguindo para baixo), enquanto o modal precisa abrir no último post lido e carregar posts anteriores e posteriores ao redor dele — isso exige o carregamento por intervalos do serviço real postStream, não apenas um callback simples de “carregar mais”.

Portanto, reutilizar PostList significaria reimplementar a maior parte do comportamento interativo de Post e o carregamento bidirecional do postStream em cima de um componente projetado para uma função diferente. Usar o componente real Post do núcleo + postStream diretamente garante que o modal se comporte de forma idêntica à página real do tópico.

3 curtidas

Consegui fazer isso funcionar com views aninhadas, precisa de mais testes e provavelmente adicionar uma opção no admin. Algumas pessoas estão usando isso, Don, e isso faz as pessoas requisitarem mais e permanecerem no site por mais tempo, já que é tão fluido e fácil de rolar, etc. Você fez um trabalho incrível com isso

4 curtidas

Testando agora! Melhoria muito impressionante na interface do usuário

2 curtidas

Precisamos desesperadamente de reforços aqui, mas com esse tempo de resposta estou um pouco preocupado agora — vocês estão vivos? :laughing:

Obrigado :sign_of_the_horns:

3 curtidas

Dúvida: por que o border-top do .topic-avatar acaba aparecendo como um separador entre a parte da postagem anterior que permanece visível durante o scrolling e o início da postagem atual? A intenção desse border-top é realmente essa, ou existe alguma regra de posicionamento/overflow fazendo com que ele tenha esse comportamento?

@media (width >= 40rem) {
    .topic-avatar {
        border-top: 1px solid var(--content-border-color);
        padding-top: var(--space-4);
        width: var(--topic-avatar-width);
        float: left;
        z-index: 2;
        height: 100%;
        overflow-anchor: none;
    }
}
1 curtida

Acho que isso poderia ser muito útil para revisar rapidamente as postagens quando você não quer esperar pelo carregamento completo da página. Adoraria se houvesse uma opção para que os cliques nas notificações abrissem o modal em vez da lista de tópicos.

Olá @Don, depois de brincar com este componente incrível por 24 horas, uma coisa que adorei foi deslizar para baixo para fechar, pois percebi que não precisava mover o polegar e a mão, e parecia muito fácil de usar o site. No entanto, em tópicos mais longos, rolar de volta para cima não era a melhor opção.

Para combater isso, adicionei um deslize para a direita para fechar no celular. Isso faz muito sentido e, para quem quer rolagem rápida etc., funciona maravilhosamente, na minha opinião. O que você acha?

O link para testar é https://www.carptalk-online.co.uk/ e, obviamente, só funciona no celular.

1 curtida