Modo escuro

Nesta página

Um site decide entre claro e escuro de duas maneiras: a preferência do sistema do visitante ou uma escolha explícita que o site armazena para aquele visitante. Os componentes EmDash leem ambos os sinais através de uma convenção no elemento <html>. Esta página descreve essa convenção, como dar a um campo de imagem uma variante escura e como renderizá-la com o componente Image de emdash/ui.

Convenção de tema

Componentes e templates usam estes dois sinais, nesta ordem:

  1. Uma classe dark ou light no <html> fixa o esquema. A classe prevalece sobre a preferência do sistema.
  2. Sem classe, o esquema segue a media query prefers-color-scheme.

Os templates incluídos armazenam uma escolha explícita em um cookie theme e a aplicam antes da primeira renderização com um script inline no <head>. O script a seguir lê o cookie e define a classe, e não faz nada quando nenhuma escolha está armazenada:

<script is:inline>
	(function () {
		var c = document.cookie;
		var i = c.indexOf("theme=");
		var theme = i >= 0 ? c.slice(i + 6).split(";")[0] : null;
		if (theme === "dark" || theme === "light") {
			document.documentElement.classList.add(theme);
		}
	})();
</script>

Defina cores uma vez com light-dark() e deixe a classe fixar o esquema:

:root {
	color-scheme: light dark;
	--color-bg: light-dark(#ffffff, #0d0d0d);
	--color-text: light-dark(#1a1a1a, #ededed);
}
:root.light {
	color-scheme: light;
}
:root.dark {
	color-scheme: dark;
}

Um site sem seletor de tema não precisa de script: deixe <html> sem classe e a preferência do sistema se aplica.

Variantes de imagem escura

Um campo de imagem pode conter uma segunda imagem para esquemas de cores escuros. Os editores a selecionam ao lado da imagem principal, e o componente Image mostra a que corresponde ao esquema do visitante.

Ativar o slot em um campo

O slot está desativado por padrão. Ative-o por campo, seja no admin ou em um arquivo seed.

No admin, abra Content Types, edite o campo de imagem e ative Dark mode variant.

Em um arquivo seed, defina a opção de widget darkVariant no campo:

{
	"slug": "featured_image",
	"label": "Featured Image",
	"type": "image",
	"options": { "darkVariant": true }
}

Escolher a variante no editor

  1. Abra uma entrada e selecione a imagem principal como de costume.

  2. Clique em Add dark mode variant abaixo da imagem e escolha a variante escura da biblioteca de mídia.

  3. Salve a entrada.

A variante é armazenada dentro do valor do campo como darkVariant. Remover a imagem principal remove a variante com ela; substituir a imagem principal mantém a variante até você substituí-la ou removê-la.

Renderizar a variante

O componente Image renderiza ambas as imagens quando o valor contém um darkVariant e mostra a correspondente com CSS. Nada muda no template:

---
import { Image } from "emdash/ui";
import { getEmDashEntry } from "emdash";

const { entry: post } = await getEmDashEntry("posts", Astro.params.slug);
---

{post?.data.featured_image && <Image image={post.data.featured_image} priority />}

A saída contém dois elementos <img>. A imagem principal recebe a classe emdash-image--light e a variante recebe emdash-image--dark. Ambas usam o texto alt, as sobreposições de largura e altura, e os atributos de carregamento da imagem principal. Cada uma mantém sua própria cor de placeholder.

Um id que você passa fica na imagem principal; a variante recebe o mesmo id com um sufixo --dark, então id="hero" produz hero e hero--dark.

Quando a imagem escura vem de outro lugar, como um segundo campo de imagem, passe-a explicitamente:

<Image image={post.data.hero} darkVariant={post.data.hero_dark} />

Comportamento de carregamento

Ambas as imagens são lazy por padrão. Navegadores não buscam uma imagem lazy que está oculta com display: none, então um visitante baixa apenas a variante para seu esquema, e a outra carrega quando o esquema muda.

Com priority, ambas as imagens recebem loading="eager" e fetchpriority="high", e ambas são baixadas em todos os esquemas. O tema é decidido no navegador, então o servidor não pode saber qual variante um visitante verá. Use priority na imagem acima da dobra e deixe outras imagens lazy.

Usar uma convenção de tema diferente

O CSS incluído oculta a variante que não corresponde ao esquema. Seus seletores usam :where() na parte <html>, então qualquer regra sua que tenha como alvo <html> com uma classe ou atributo prevalece.

Se seu seletor define um atributo como data-theme, a correção mais curta é também definir as classes dark e light do mesmo caminho de código. Caso contrário, sobrescreva os quatro casos em sua própria folha de estilos:

:root[data-theme="dark"] .emdash-image--light,
:root[data-theme="light"] .emdash-image--dark {
	display: none;
}
:root[data-theme="dark"] .emdash-image--dark,
:root[data-theme="light"] .emdash-image--light {
	display: block;
}

Combine o valor display com o que sua folha de estilos dá às imagens em outros lugares, por exemplo inline quando você não reseta img para block.