Modo Escuro

Nesta página

Um site decide entre claro e escuro de uma entre duas maneiras: a preferência do sistema do visitante ou uma escolha explícita que o site armazena para aquele visitante. Os componentes do EmDash leem ambos os sinais por meio de uma única 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 em <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 as cores uma única 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 alternador de tema não precisa de script: deixe <html> sem classe e a preferência do sistema se aplica.

Variantes escuras de imagem

Um campo de imagem pode ter uma segunda imagem para esquemas de cores escuros. Os editores a escolhem 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 vem desativado por padrão. Ative-o por campo, 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 contraparte escura na 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 junto; substituir a imagem principal mantém a variante até que você a substitua ou remova.

Renderizar a variante

O componente Image renderiza ambas as imagens quando o valor tem uma darkVariant e mostra a correspondente com CSS. Nada muda no template:

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

const slug = decodeSlug(Astro.params.slug);

if (!slug) {
  return Astro.rewrite("/404");
}

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

if (!post) {
  return Astro.rewrite("/404");
}
---

{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 da imagem principal, as substituições de largura e altura e os atributos de carregamento. Cada uma mantém sua própria cor de placeholder.

Um id que você passar permanece na imagem principal; a variante recebe o mesmo id com o sufixo --dark, de modo que id="hero" resulta em 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. Os navegadores não buscam uma imagem lazy oculta com display: none, portanto o visitante baixa apenas a variante do seu esquema, e a outra é carregada quando o esquema muda.

Com priority, ambas as imagens recebem loading="eager" e fetchpriority="high", e as duas são baixadas em qualquer esquema. O tema é decidido no navegador, então o servidor não consegue saber qual variante o visitante verá. Use priority na única imagem acima da dobra e deixe as demais imagens como lazy.

Usar uma convenção de tema diferente

O CSS fornecido oculta a variante que não corresponde ao esquema. Seus seletores usam :where() na parte do <html>, de modo que qualquer regra sua que mire <html> com uma classe ou um atributo prevalece.

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

: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;
}

Faça o valor de display corresponder ao que a sua folha de estilo dá às imagens em outros lugares, por exemplo inline quando você não redefine img para block.