다크 모드

이 페이지

사이트는 두 가지 방법 중 하나로 라이트와 다크를 결정합니다. 방문자의 시스템 환경설정, 또는 사이트가 해당 방문자를 위해 저장한 명시적인 선택입니다. EmDash 컴포넌트는 <html> 요소의 하나의 규칙을 통해 두 신호를 모두 읽습니다. 이 페이지에서는 그 규칙, 이미지 필드에 다크 변형을 지정하는 방법, 그리고 emdash/ui의 Image 컴포넌트로 렌더링하는 방법을 설명합니다.

테마 규칙

컴포넌트와 템플릿은 다음 순서로 이 두 신호를 사용합니다.

  1. <html>의 dark 또는 light 클래스가 스키마를 고정합니다. 이 클래스는 시스템 환경설정보다 우선합니다.
  2. 클래스가 없으면 스키마는 prefers-color-scheme 미디어 쿼리를 따릅니다.

번들 템플릿은 명시적인 선택을 theme 쿠키에 저장하고, <head>의 인라인 스크립트로 첫 페인트 전에 적용합니다. 다음 스크립트는 쿠키를 읽어 클래스를 설정하며, 저장된 선택이 없으면 아무 작업도 하지 않습니다.

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

light-dark()로 색상을 한 번만 정의하고, 클래스로 스키마를 고정하세요.

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

테마 전환기가 없는 사이트에는 스크립트가 필요 없습니다. <html>에 클래스를 두지 않으면 시스템 환경설정이 적용됩니다.

다크 이미지 변형

이미지 필드는 다크 색상 스키마용 두 번째 이미지를 가질 수 있습니다. 편집자는 기본 이미지 옆에서 이를 선택하고, Image 컴포넌트는 방문자의 스키마에 맞는 이미지를 표시합니다.

필드에서 슬롯 활성화하기

슬롯은 기본적으로 꺼져 있습니다. 필드별로 관리자 또는 시드 파일에서 켤 수 있습니다.

관리자에서는 Content Types를 열고 이미지 필드를 편집한 다음 Dark mode variant를 켭니다.

시드 파일에서는 필드에 darkVariant 위젯 옵션을 설정합니다.

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

편집기에서 변형 선택하기

  1. 항목을 열고 평소처럼 기본 이미지를 선택합니다.

  2. 이미지 아래의 Add dark mode variant를 클릭하고 미디어 라이브러리에서 다크용 이미지를 선택합니다.

  3. 항목을 저장합니다.

변형은 필드 값 안에 darkVariant로 저장됩니다. 기본 이미지를 제거하면 변형도 함께 제거됩니다. 기본 이미지를 교체하면 변형을 교체하거나 제거할 때까지 변형이 유지됩니다.

변형 렌더링하기

값에 darkVariant가 있으면 Image 컴포넌트는 두 이미지를 모두 렌더링하고 CSS로 일치하는 이미지를 표시합니다. 템플릿은 바꿀 필요가 없습니다.

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

출력에는 두 개의 <img> 요소가 포함됩니다. 기본 이미지에는 클래스 emdash-image--light가, 변형에는 emdash-image--dark가 붙습니다. 둘 다 기본 이미지의 alt 텍스트, 너비와 높이 재정의, 로딩 속성을 사용합니다. 각 이미지는 자신만의 플레이스홀더 색상을 유지합니다.

전달한 id는 기본 이미지에 그대로 유지됩니다. 변형은 같은 id에 --dark 접미사가 붙으므로, id="hero"는 hero와 hero--dark가 됩니다.

다크 이미지가 두 번째 이미지 필드처럼 다른 곳에서 온다면 명시적으로 전달하세요.

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

로딩 동작

두 이미지는 기본적으로 지연 로딩됩니다. 브라우저는 display: none으로 숨겨진 지연 로딩 이미지를 가져오지 않으므로, 방문자는 자신의 스키마에 맞는 변형만 다운로드하고, 다른 하나는 스키마가 바뀔 때 로드됩니다.

priority를 사용하면 두 이미지 모두 loading="eager"와 fetchpriority="high"를 갖게 되며, 어떤 스키마에서든 둘 다 다운로드됩니다. 테마는 브라우저에서 결정되므로 서버는 방문자가 어떤 변형을 보게 될지 알 수 없습니다. priority는 첫 화면에 보이는 이미지 하나에만 사용하고 나머지 이미지는 지연 로딩으로 두세요.

다른 테마 규칙 사용하기

기본 제공 CSS는 스키마와 일치하지 않는 변형을 숨깁니다. 선택자는 <html> 부분에 :where()를 사용하므로, 클래스나 속성으로 <html>을 대상으로 하는 사용자의 모든 규칙이 우선합니다.

전환기가 data-theme 같은 속성을 설정한다면, 가장 간단한 해결책은 같은 코드 경로에서 dark와 light 클래스도 함께 설정하는 것입니다. 그렇지 않다면 자신의 스타일시트에서 네 가지 경우를 재정의하세요.

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

display 값은 스타일시트가 다른 곳에서 이미지에 지정하는 값과 맞추세요. 예를 들어 img를 block으로 재설정하지 않았다면 inline을 사용합니다.