Modalità Scura

In questa pagina

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

Convenzione del tema

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

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

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

<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 sola volta con light-dark() e lascia che sia la classe a fissare 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 selettore del tema non ha bisogno di alcuno script: lascia <html> senza classe e si applica la preferenza di sistema.

Varianti scure delle immagini

Un campo immagine può avere una seconda immagine per gli schemi di colore 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 del 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. Fai clic su Add dark mode variant sotto l’immagine e scegli la controparte 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 finché 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 tramite CSS. Nel template non cambia nulla:

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

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

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

Quando l’immagine scura proviene da un’altra fonte, ad esempio 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 nascosta con display: none, quindi un visitatore scarica solo la variante per il proprio schema, e l’altra viene caricata quando lo schema cambia.

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

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 tua regola che punta a <html> con una classe o un attributo ha la precedenza.

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

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

Fai corrispondere il valore di display a quello che il tuo foglio di stile assegna altrove alle immagini, ad esempio inline quando non reimposti img a block.