어두운 모드 전환을 사용하면 사용자가 한 사이트의 시스템 색 구성표를 재정의할 수 있습니다.
재정의는 <meta name="color-scheme">를 업데이트하여 적용됩니다. light dark는 '시스템 환경설정 따르기'를 의미하고 light 또는 dark는 명시적 스키마를 고정합니다.
색상 (light-dark()), 시스템 색상, 네이티브 UI는 모두 CSS color-scheme 속성을 기반으로 하므로 전환 버튼은 이 속성을 <meta>와 동기화된 상태로 유지하여 작동합니다(CSS에 재정의 반영 참고).
영구 전환, 설정 제어, 전환 없음 중 어떤 것이 적합한가요?
대부분의 사이트에서는 사이트 UI (헤더, 탐색, 바닥글 등)에 영구적인 색 구성표 전환이 필요하지 않습니다. 기본적으로 시스템 환경설정을 따르고, 수동 재정의가 필요한 경우 설정을 별도의 설정 표시 경로(설정 페이지, 패널 또는 오버레이)에 배치합니다.
OS에 스키마를 고정하는 것은 합법적이지만 드문 의도이며, 드문 의도는 항상 표시되는 컨트롤이 아니라 점진적 공개 뒤에 속합니다.
영구 전환은 주로 개발자 대상 또는 개발자 인접 (예: 디자이너용) 사이트에서 사용되며, 이러한 사이트에서는 스키마 전환이 일반 웹 사용자보다 훨씬 더 자주 발생합니다. 개발자 사이트에서 일반적인 패턴이라고 해서 추가하지 마세요. 이 컨트롤에 대한 개발자의 관심은 일반적인 사용자를 대표하지 않습니다.
구현
먼저 어두운 모드의 모든 단계를 따라 두 스키마를 모두 지원하고 시스템 환경설정을 기본값으로 설정합니다.
데이터 모델
어두운 모드 전환의 데이터 모델에는 일반적으로 세 가지 상태가 포함됩니다.
- 시스템 (저장된 값 없음): UI가 시스템 색 구성표를 따릅니다. 이상적으로는 이 값이 기본값이어야 합니다.
- 밝은 테마(
"light"로 저장됨): UI는 시스템 색 구성표와 관계없이 밝은 테마를 사용합니다. - 어두운 테마(
"dark"로 저장됨): UI는 시스템 색 구성표와 관계없이 어두운 테마를 사용합니다.
이 상태는 일반적으로 localStorage에 저장되며 문서 헤드의 <meta name="color-scheme"> 요소에 반영됩니다.
CSS에 재정의 반영
<meta>는 프레젠테이션 힌트일 뿐입니다. 작성자 CSS의 color-scheme 선언은 이를 재정의합니다.
어두운 모드는 :root에서 color-scheme: light dark를 설정하므로 <meta>만 업데이트하면 작업이 실행되지 않습니다. CSS가 우선 적용됩니다.
<meta>를 속성에 다시 매핑하여 연쇄가 이를 따르도록 합니다.
:root {
color-scheme: light dark;
&:has(> head > meta[name="color-scheme"][content="dark"]) {
color-scheme: dark;
}
&:has(> head > meta[name="color-scheme"][content="light"]) {
color-scheme: light;
}
}
color-scheme: light 또는 color-scheme: dark를 루트 기본값으로 하드코딩하지 마세요. 기본 선언은 light dark로 유지되어야 합니다(MUST).
CSS는 JS에 종속되면 안 됩니다(MUST NOT). JS가 실행되지 않으면 <meta>가 light dark로 유지되고 사이트가 시스템 환경설정을 따르므로 아무것도 중단되지 않습니다.
지속성 및 FOUC 방지
localStorage에서 재정의를 유지합니다. 시스템 기본값으로 돌아갈 때 항목을 삭제합니다.
잘못된 스키마가 깜박이지 않도록 하려면 <meta> 요소 바로 뒤에 있는 인라인 스크립트(type=module 아님, defer 아님)에 저장된 값을 적용합니다.
<meta name="color-scheme" content="light dark">
<script>
document.querySelector('meta[name="color-scheme"]').content = localStorage.getItem("color-scheme") ?? "light dark";
</script>
- 이 인라인 스크립트는 FOUC 방지 만을 위해 존재합니다. 가능한 한 작게 유지합니다. 색 구성표 전환을 처리하기 위한 추가 JS는 나중에 로드할 수 있습니다.
- 드문 경우지만
localStorage액세스에서 오류가 발생할 수 있습니다 (예: 사이트 데이터 차단). 하지만 이 스크립트는 다른 작업을 수행하지 않으므로 유일한 부작용은 콘솔 오류입니다. 이 문제가 중요한 경우try .. catch로 래핑합니다. 테마를 유지할 수 없더라도 페이지에 계속 적용되도록 합니다. - 재정의는 다른 탭에서 변경할 수 있습니다. 동기화를 유지하려면
window의storage이벤트를 처리하세요. - 시스템 환경설정이 일치하도록 변경될 때 저장된 값을 삭제하기 위해
matchMedia()를 사용하지 마세요. 많은 사용자의 OS는 시간에 따라 스키마를 자동으로 전환합니다. 두 스키마가 일치할 때마다 저장된 값을 삭제하면 스키마를 고정할 수 없습니다.
HTML 및 색상이 아닌 값의 분기
색상은 추가 작업이 필요하지 않습니다. <meta>가 color-scheme에 매핑되면(위 참고) light-dark()는 재정의를 따릅니다.
이 섹션에서는 다른 값 (예: font-weight, 미디어 소스 등)을 조정하는 방법을 설명합니다.
페이지가 시스템 환경설정을 따르는 경우 (prefers-color-scheme: dark)를 사용하여 <picture> 또는 <video> 소스를 포함한 분기를 만들 수 있습니다.
색 구성표를 재정의하면 시스템 환경설정이 더 이상 정보 소스가 아니며 미디어 쿼리만 허용하는 API의 경우 직접적인 대안이 없다는 추가적인 문제가 발생합니다.
JS가 로드되지 않고 OS 기본값이 어두운 경우 잘못될 수 있으므로 분기를 위해 .dark와 같은 JS 적용 클래스에만 의존하지 마세요.
미디어 쿼리를 :root:has(> head > meta[name="color-scheme"][content="dark"]) (또는 light) 선택기와 결합하여 분기할 수 있습니다.
이를 깔끔하게 구현하는 방법에 관한 자세한 내용은 선택기 및 @-rule 조합을 참고하세요.
컨트롤 자체의 옵션도 색상이 아닌 분기입니다. 마크업에 두 상태 (아이콘과 시각적으로 숨겨진 작업 텍스트, 예: '어두운 테마로 전환')를 모두 포함하고 이러한 선택기를 통해 비활성 상태를 숨깁니다.
display: none는 AT에서 중복 옵션도 삭제하므로 시스템 환경설정이 변경될 때 컨트롤을 최신 상태로 유지하는 데 JS가 필요하지 않습니다.
HTML 내 미디어의 경우 현재로서는 두 버전을 별도의 요소로 포함하고 공개 상태를 적절하게 전환하는 방법밖에 없습니다.
display: none로 <source> 요소를 숨기는 것은 작동하지 않습니다.
사용자 인터페이스
2~3개 주?
모든 경우에 UI는 색 구성표를 시스템으로 되돌리거나 설정하는 것을 지원해야 합니다(MUST).
공간이 제한되어 있고 설정을 조정하는 것이 일반적으로 사용자 목표와 무관한 사이트 크롬 (헤더, 탐색, 바닥글 등)의 영구 전환 버튼과 같이 공간이 제한된 전환 버튼에서는 두 가지 상태를 표시하는 것이 좋습니다.
별도의 설정 표시 경로(페이지, 패널 또는 오버레이)에 있는 색 구성표 컨트롤은 다른 시나리오이며 세 가지 상태('밝게', '어둡게', '시스템')를 모두 명시적으로 노출할 수 있습니다(MAY). 여기에서 사용자는 이미 향후 동작에 관해 신중하게 결정을 내리고 있으며 옵션을 설명할 여지가 있습니다.
사이트에서 컨텍스트 인식 스키마 (예: OS가 어두울 때 더 어두운 밝은 모드)를 구현하는 경우에도 세 가지 명시적 상태가 보장됩니다. 이 경우 '밝은 모드'와 '시스템 (현재 밝은 모드)'이 실제로 다르기 때문입니다.
두 가지 상태 전환 버튼
이러한 제어 기능이 사용되는 경우 현재 상태를 장기적으로 유지하기보다는 일시적인 편의 조정 ('지금 너무 밝음')인 경향이 있습니다. 따라서 두 가지 상태 전환의 경우 세 가지 설정 중 두 가지를 표시하는 것으로 충분합니다. 즉, 시스템(설정을 되돌릴 수 있도록)과 시스템의 현재 반대 상태(현재 리터럴 값으로 저장됨)를 표시하면 됩니다.
다음 두 가지 상태만 있어야 합니다.
- 시스템 기본값 — 저장된 값이 없으며
<meta>콘텐츠light dark입니다. 현재 확인된 값으로 표시됩니다 (예: 밝을 때는 태양 아이콘). - 재정의 — 문자 그대로
light또는dark로 저장됩니다.
기본적으로 이는 현재 시스템 환경설정과 일치하는 명시적 상태에 도달할 수 없는 3가지 상태 컨트롤 (light dark, light, dark)입니다.
사용자가 전환하는 경우:
- 타겟 스킴 = 현재 렌더링된 스킴의 반대 (저장된 값이 있는 경우 저장된 값, 없는 경우 시스템 환경설정) 사용자 의도는 '시스템 기본값의 반대를 선택'하는 것이 아니라 '실시간으로 표시되는 것의 반대를 선택'하는 것입니다.
- 타겟이 현재 시스템 환경설정과 다른 경우 문자 그대로 저장합니다.
- 타겟이 현재 시스템 환경설정과 일치하면 사용자가 조정을 실행취소하는 것입니다. 저장된 값을 삭제합니다. 저장하지 마세요. 저장하면 향후 시스템 변경사항에 대해 스키마가 보이지 않게 고정됩니다.
<meta>콘텐츠를 저장된 값으로 설정하거나, 값이 없는 경우light dark로 설정합니다.
중요: 차이는 저장 시에만 확인해야 하며 소급해서는 안 됩니다. 시스템 환경설정이 나중에 일치하도록 변경하는 저장된 값은 유지해야 합니다(MUST). 많은 사용자의 OS는 시간에 따라 스키마를 자동으로 전환합니다. 두 스키마가 일치할 때마다 저장된 값을 삭제하면 스키마를 고정할 수 없습니다.
OS가 밝은 모드로 설정된 경우의 예시 시나리오:
- 사용자가 전환합니다. 어두운 모드는 시스템 환경설정과 다르므로
dark가 저장되고 사이트가 어두운 모드로 전환됩니다. - OS 설정이 어두운 모드로 변경됩니다. 사이트가 어두운 상태로 유지됩니다 (저장된 값이 이제 시스템 환경설정과 일치하지만 유지됨).
- OS 설정이 다시 밝은 모드로 변경됩니다. 사이트가 어둡게 유지됩니다.
- 사용자가 전환합니다. 타겟 (밝게)이 시스템 환경설정과 일치하므로 저장된 값이 삭제되고 사이트가 다시 시스템을 따릅니다.
3상태 전환 버튼
공간이 더 많고 사용자가 이미 장기 환경설정을 설정하는 마음가짐인 경우 3가지 상태 컨트롤을 사용하면 더 명확하게 표현할 수 있습니다. 이 경우 데이터 모델은 시스템, 밝게, 어둡게라는 세 가지 UI 옵션에 깔끔하게 매핑됩니다.
인지 부하를 줄이기 위해 '시스템' 옵션은 현재 시스템 설정도 표시해야 합니다. 예를 들면 다음과 같습니다.
- 아이콘(예: 시스템 아이콘 오른쪽 하단의 태양 아이콘)
- 텍스트(예: '시스템(현재 밝은 테마)')