manualdousuario / dez

Tema oficial do blog Manual do Usuário.
https://manualdousuario.net/
Other
7 stars 3 forks source link

Tema escuro não funciona com JavaScript desativado #45

Closed rghedin closed 7 months ago

rghedin commented 7 months ago

É algo super específico; eu mesmo jamais pegaria esse bug. (Foi reportado por um leitor.)

Para reproduzi-lo, basta alterar a aparência do sistema operacional para o modo escuro com o JavaScript do navegador desativado. Desse modo, o site fica sempre claro — ele não acompanha a mudança da aparência do sistema e o seletor claro/escuro não funciona.

Seria legal, com o JavaScript desativado, que pelo menos o site acompanhasse a aparência em uso no sistema.

breakzplatform commented 7 months ago

OI @rghedin!

O tl;dr é: isso não é um bug, isso é uma consequência da escolha de ter um seletor de tema definido pelo usuário.

Agora vamos a parte longa, pois certamente sei que não é a resposta que você gostaria de ler. Eu vou tentar da melhor forma possível. Se e onde eu falhar, me diga que tento ser mais claro.

Num site sem escolha de tema, como era o Manual, o tema era definido diretamente pelo navegador lendo a informação do sistema operacional de qual era a preferência do usuário. No CSS, existe o escopo geral, que no caso do Manual se refere ao tema claro, e o escopo do tema escuro, dentro de um media query (prefers-color-scheme: dark), que alterava as regras pra o tema escuro.

Até ai, sem mistério. É como efetivamente tudo se comporta de forma vanilla.

A propriedade prefers-color-scheme: dark é somente leitura. Isso significa que não tem nenhuma forma de eu, como website, sobrescrever essa regra.

Ao ter um seletor de tema nós escolhemos não deixar o navegador ser responsável por essa tarefa e sim o próprio website. Uma das primeiras modificações que fiz foi, ao invés de usar o prefers-color-scheme: dark mudarmos todas as classes para [data-theme="dark"], pois isso é algo que eu como website consigo controlar.

Então consigo verificar se o usuário tem uma preferência específica. Caso tenha, uso ela. Do contrário, leio o valor do sistema operacional e uso ele.

E eu controlo isso... com JavaScript. Não tem jeito. Se não tem JavaScript, não tem seletor.

E não é questão da nossa implementação. Você pode procurar aí implementação de seletores dark mode que parecem que funcionam, mas apenas como prova de conceito. Não há, efetivamente, como fazer um seletor funcional sem JavaScript.

Mas você pode me perguntar, ah, mas uma vez que sei que o JavaScript está desabilitado, será que não dá pra voltar pra forma "nativa"?

Não. Tudo que é dinâmico no front-end é por JavaScript. Não é possível, por exemplo, pegar o modo escuro/claro do usuário por meio do backend, que seria via o PHP do WordPeress. Então, em tese, eu teria que saber cse o usuário tem o modo escuro ou claro e também não tem JavaScript antes da página ser renderizada, pra entregar a ele um conteúdo diferente. E eu, como website, não consigo fazer isso.

(Um adendo aqui: o Chrome, até passa essa info se você pedir com jeitinho, mas em outros navegadores você está sem sorte. De qualquer forma, eu ainda precisaria saber se o usuário tem JS ou não habilitado, e isso não é possível - e nunca deveria ser, mesmo)

Pô, mas eu não posso fazer NADA então?

Sempre dá pra fazer alguma coisa. A questão é se faz sentido e vale o esforço. O que eu enxergo de possível é usar a tag <noscript> e fazer algo do tipo:

<noscript>
  <style>
    @media (prefers-color-scheme: dark) {
        #dark-mode-toggle li a {
          /* CC-BY https://www.svgrepo.com/svg/532889/sun */
          background-image: url('/wp-content/themes/dez/img/icone-dark-mode-sol.svg');
          filter: invert();
        }

        /* repetir todo conteúdo do dark mode aqui, sem a tag [data-theme="dark"]  */
    }
  </style>
</noscript>

Particularmente não vejo futuro em seguir dessa forma, dado que todas as vezes você estaria escrevendo o CSS em 2 lugares. Isso seguiria para todas alterações no modo escuro. Dá pra automatizar isso mas de novo, é um esforço que tem que calcular se vale a pena.

Sinceramente, pra mim, apensar de chato, há consequências de não deixar o JavaScript ativo e só do site do Manual carregar e não quebrar, já é muita coisa, pois quem navega na web atual dessa forma já deve estar acostumado a lidar com problemas.

Então, em resumo, mais uma vez, não é um bug. É consequência. E não creio que nesse caso tão específico valha o esforço de tornar a funcionalidade mais abrangente pra cobrir esse caso.

rghedin commented 7 months ago

Obrigado pela aula, @breakzplatform! Concordo contigo, acho que não vale o esforço. Dito isso, vou fechar a issue.

fulalas commented 7 months ago

Obrigado pelo esforço, pessoal!

Eu só me questiono 2 coisas então: 1- por que até há umas 2 ou 3 semanas isso funcionava (i.e. tema dark com JS desativado)? 2- por que não colocam o tema dark como padrão caso não haja JS disponível? Isso faz sentido, afinal quem desativa JS certamente é um usuário avançado, e a maioria deles prefere tema dark ;)

rghedin commented 7 months ago

@fulalas, você pode aqui!

1) Porque antes não havia JavaScript controlando a aparência do site. Usávamos uma solução nativa do navegador, que segue a aparência do sistema, mas que, por outro lado, não deixa que o usuário altere o padrão.

2) Poxa, se existe um padrão, a primeira coisa que alguém vê ao entrar no site, prefiro que seja o tema claro. É o que considero o “oficial”.

Uma gambiarra, em casos como o seu, pode ser usar uma extensão como a Dark Reader. Não testei, mas talvez ela consiga alterar a aparência do Manual com JavaScript desativado.

fulalas commented 7 months ago

Usávamos uma solução nativa do navegador, que segue a aparência do sistema

OK. E se no caso de não haver JS vocês derem fallback pra essa solução antiga?

rghedin commented 7 months ago

O @breakzplatform pode responder melhor, mas, pelo que entendi, não tem como manter os dois sistemas mesmo em fallback.

breakzplatform commented 7 months ago

oi @fulalas!

Seu questionamento é super válido e natural, tanto que eu mesmo usei este exato questionamento lá em cima: "Mas você pode me perguntar, ah, mas uma vez que sei que o JavaScript está desabilitado, será que não dá pra voltar pra forma "nativa"?"

Tentando deixar mais claro: não é possível site saber se o usuário não tem JavaScript ou não ativo. Não dá pra obter essa informação puramente via backend, pois essa informação na parte do cliente, no próprio navegador, após renderizar a página. E não dá pelo frontend, pois, bem, não tem JavaScript!

E isso é ótimo! Não sabendo disso, os sites não podem entregar um conteúdo diferente, o que poderia facilmente comprometer a segurança do usuário, fazer tracking dele, forçar a exibir anúncios direcionados e tudo mais (um dos principais motivos de se desligar o JavaScript).

Como pra tudo tem um jeito, também lá em cima eu demonstrei como seria pra usar a tag <noscript> pra "emular" esse fallback. A tag <noscript> tem seu conteúdo renderizado quando não há JavaScript, mas é HTML, então é pura marcação, não dá pra programar nada.

O "fallback" então seria feito usando CSS dentro do <noscript>. Porém, é bastante custoso e trabalhoso de manter, pois basicamente seria necessário escrever 2 dark-modes. Ou seja, é uma solução possível, mas não viável.

Um outro cenário possível que acabei de pensar, que vai pra uma complexidade totalmente diferente, é por exemplo para os usuários cadastrados, eles poderem em alguma configuração em algum lugar definir sua preferência de tema e salvar ela no banco de dados ou via cookie. Essa informação salva no banco é acessível pelo backend e ai sim ela poderia ser usada e funcionaria pra fazer um condicional via backend: continuaríamos a não saber se ele tem JS ou não ativo, mas com a informação salva poderíamos entregar o modo "certo" pra cada usuário.

Mas, igualmente, também acho uma solução possível, mas não viável. Ao menos fica ai pro @rghedin pensar na possibilidade.

Bônus: eu testei aqui o Dark Mode sem JS ativo e ele... funciona? Não fica excelente, mas no texto, quebra um bom galho.

fulalas commented 7 months ago

@breakzplatform, obrigado pela mensagem detalhada.

Eu continuo não entendendo como antes isso funcionava mesmo com o JavaScript desativado. Era tudo feito 100% via CSS, creio. Vocês por acaso removeram esse CSS? Ou só adaptaram pra usá-lo quando o método JS assim determinar?

De repente eu poderia pegar esse código antigo e fazer uma extensão pra mim mesmo. Tema bright é dose pra leão, ainda mais em OLED, hahaha!

Sabe o que é meio irônico? É que estamos falando aqui no Github, onde o tema dark funciona, mesmo sem JS nem cookie, hehehe!

rghedin commented 7 months ago

Sim, tivemos que mudar o CSS para o seletor em JavaScript funcionar, @fulalas. Aqui você pode ver as diferenças. É por isso que antes funcionava e agora, não.

Note que, como o @breakzplatform disse, é uma escolha. O site da 404 Media, por exemplo, tem o mesmo comportamento do do Manual. O O GitHub deve usar uma solução persistente, vinculada à conta do usuário, o que talvez explique o funcionamento sem JavaScript e a opção de sobrepôr a aparência do sistema.

fulalas commented 7 months ago

Maravilha, Ghedin! Criei uma extensão aqui (meio porca, admito) que fez funcionar. Vou deixar o código caso alguém se interesse:

manifest.json:

{
    "manifest_version": 2,
    "name": "MdU-DarkTheme",
    "description": "Enable dark theme in MdU",
    "version": "1.0",

    "applications": {
        "gecko": {
            "id": "{fcaf8e22-fd89-4a3f-80a0-c49fff1c4ac8}"
        }
    },

    "content_scripts": [{
        "run_at": "document_start",
        "matches": ["https://*.manualdousuario.net/*"],
        "js": ["content.js"]
    }],

    "permissions": ["webRequest", "https://*.manualdousuario.net/*"]
}

content.js:

document.addEventListener('DOMContentLoaded', function () {
    const styleTags = document.getElementsByTagName('style')
    const style = document.createElement('style')
    style.textContent = css
    document.head.appendChild(style)
});

const css = `
@media (prefers-color-scheme: dark) {
 :root {
  --cor-pagina:hsl(0, 0%, 1%);
 --cor-fonte:hsl(0, 0%, 90%);
 --cor-link-meta:rgb(194, 210, 255);
 --cor-link-meta-est:rgb(235, 240, 255);
 --cor-link-ori:rgb(108, 147, 255);
 --cor-link-vis:rgb(171, 136, 255);
 --cor-link-est:rgb(166, 189, 255);
 --cor-bordas:rgb(68, 68, 68);
 --cor-caixas:rgb(29, 29, 29)
}
.gridicon,
 .menu-toggle-icon,
 .site-title img,
 a.comment-link {
 filter:invert();
 fill:unset
}
 .dfad_pos_1 {
 box-shadow:none
}
 .dfad_pos_1 span {
 color:#86fd86;
 border-color:#86fd86
}
 .dfad_pos_1 span.calhau {
 color:#ffdbdb;
 border-color:#ffdbdb
}
 .activitypub-comment .comment-meta {
 background-color:#00003d
}
 .activitypub-comment>.comment-body>footer>.comment-author>.fn:after {
 filter:invert(1)
}
 a.comment-link span {
 filter:invert()
}
 blockquote:before {
 opacity:.13
}
 input[type=email],
 input[type=number],
 input[type=search],
input[type=text],
input[type=url],
 textarea {
 background-color:var(--cor-caixas);
 color:var(--cor-fonte)
}
 .ctx-atencao {
 background-color:#1f1c00
}
 .ctx-transparencia {
 background-color:#001425
}
 .ctx-dica {
 background-color:#2e0f00
}
 .podcast_player {
 background-color:#100024
}
 .blog article.category-aplicativos {
 background-color:#000c2d
}
 .top:before {
 border-top:5px solid var(--cor-caixas);
 border-right:5px solid var(--cor-caixas)
}
@media (max-width:620px) {
  .category-aplicativos p:first-child {
  background-color:hsl(224,100%,7.5%)
 }
}
}`
rghedin commented 7 months ago

Se duvidar, dá para transformar isso em um userScript, não?

fulalas commented 7 months ago

Desculpe-me a minha ignorância, mas o que seria userScript?

fulalas commented 7 months ago

@breakzplatform, obrigado pela sugestão. A extensão agora ficou bem mais simples e segura. :)

Com relação ao Github, ele pode até salvar o tema na configuração do usuário, mas mesmo se eu entrar deslogado, com JavaScript e cookies desativados, o tema preto é acionado (porque meu sistema está com tema preto).

breakzplatform commented 7 months ago

@fulalas o GitHub não tem a mesma forma de seletor que o Manual usa. Se o usuário está deslogado ele vai seguir o sistema (só com CSS) se o usuário está logado ele pega a preferência salva do backend.

Não á seletor deslogado, como o Manual faz.

Essa possibilidade foi uma das que levantei acima, mas isso restringiria o seletor a usuários cadastrados do Manual, o que pra um blog não faz muito sentido.

É realmente uma questão de escolha.

breakzplatform commented 7 months ago

Não sei porque meu comentario anterior sumiu, mas segue novamente:

Como uma extensão usa JavaScript pra funcionar, e roda esse JavaScript próprio mesmo com o JavaScript dos sites desabilitado, você pode usar o próprio seletor do Manual pra executar a função pra você. Dessa forma, você não precisa ficar reescrevendo o CSS e nunca vai precisar atualizar ele se o CSS do Manual mudar no futuro.

Ou seja, seu content.js bastaria ser isso aqui:

document.documentElement.dataset.theme = "dark";

Sim. Só essa linha. E aí então estaria forçando a usar um dark mode. É exatamente o que o código do seletor faz.

@rghedin tá na mão o UserScript disso. Porém, ao menos nos meus testes, a Tampermonkey não rodou o JavaScript com ele desabilitado.

// ==UserScript==
// @name         MdU Force Dark Theme
// @namespace    http://tampermonkey.net/
// @version      2024-03-07
// @description  Set the theme of the manualdousuario.net website to dark
// @author       Joseli.to
// @match        https://manualdousuario.net/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    document.documentElement.dataset.theme = "dark";
})();

E uma curiosidade final é que, sim, o GitHub salva as preferências de tema na conta do usuário.