Modalità Scura

In questa pagina

Un sito decide tra chiaro e scuro in uno dei due modi: la preferenza di sistema del visitatore, o una scelta esplicita che il sito memorizza per quel visitatore. I componenti EmDash leggono entrambi i segnali attraverso una convenzione sull’elemento <html>. Questa pagina descrive quella convenzione, come dare a un campo immagine una variante scura, e come renderizzarla con il componente Image da emdash/ui.

Convenzione del tema

I componenti e i template usano questi due segnali, in questo ordine:

  1. Una classe dark o light su <html> fissa lo schema. La classe vince sulla preferenza di sistema.
  2. Senza classe, lo schema segue la media query prefers-color-scheme.

I template forniti memorizzano una scelta esplicita in un cookie theme e la applicano prima del primo rendering con uno script inline nel <head>. Lo script seguente legge il cookie e imposta la classe, e non fa nulla quando nessuna scelta è memorizzata:

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

Definisci i colori una volta con light-dark() e lascia che la classe fissi lo schema:

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

Un sito senza commutatore di tema non ha bisogno di script: lascia <html> senza classe e si applica la preferenza di sistema.

Varianti scure delle immagini

Un campo immagine può portare una seconda immagine per gli schemi di colori scuri. Gli editor la scelgono accanto all’immagine principale, e il componente Image mostra quella che corrisponde allo schema del visitatore.

Abilitare lo slot su un campo

Lo slot è disattivato per impostazione predefinita. Attivalo per campo, nell’admin o in un file seed.

Nell’admin, apri Content Types, modifica il campo immagine e attiva Dark mode variant.

In un file seed, imposta l’opzione widget darkVariant sul campo:

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

Scegliere la variante nell’editor

  1. Apri una voce e seleziona l’immagine principale come al solito.

  2. Clicca su Add dark mode variant sotto l’immagine e scegli la variante scura dalla libreria multimediale.

  3. Salva la voce.

La variante è memorizzata all’interno del valore del campo come darkVariant. Rimuovere l’immagine principale rimuove anche la variante; sostituire l’immagine principale mantiene la variante fino a quando non la sostituisci o la rimuovi.

Renderizzare la variante

Il componente Image renderizza entrambe le immagini quando il valore contiene una darkVariant e mostra quella corrispondente con CSS. Nulla cambia nel template:

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

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

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

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

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

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

L’output contiene due elementi <img>. L’immagine principale ottiene la classe emdash-image--light e la variante ottiene emdash-image--dark. Entrambe usano il testo alternativo, le sovrascritture di larghezza e altezza, e gli attributi di caricamento dell’immagine principale. Ognuna mantiene il proprio colore segnaposto.

Un id che passi rimane sull’immagine principale; la variante ottiene lo stesso id con un suffisso --dark, quindi id="hero" produce hero e hero--dark.

Quando l’immagine scura viene da un’altra fonte, come un secondo campo immagine, passala esplicitamente:

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

Comportamento di caricamento

Entrambe le immagini sono lazy per impostazione predefinita. I browser non scaricano un’immagine lazy che è nascosta con display: none, quindi un visitatore scarica solo la variante per il suo schema, e l’altra si carica quando lo schema cambia.

Con priority, entrambe le immagini ottengono loading="eager" e fetchpriority="high", e entrambe vengono scaricate in ogni schema. Il tema è deciso nel browser, quindi il server non può sapere quale variante un visitatore vedrà. Usa priority sull’unica immagine above-the-fold e lascia le altre immagini lazy.

Usare una convenzione di tema diversa

Il CSS fornito nasconde la variante che non corrisponde allo schema. I suoi selettori usano :where() sulla parte <html>, quindi qualsiasi regola tua che punti a <html> con una classe o un attributo vince.

Se il tuo commutatore imposta un attributo come data-theme, la soluzione più breve è impostare anche le classi dark e light dallo stesso percorso di codice. Altrimenti, sovrascrivi i quattro casi nel tuo stylesheet:

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

Abbina il valore display a quello che il tuo stylesheet dà alle immagini altrove, per esempio inline quando non resetti img a block.