color-scheme 속성은 페이지에서 지원하는 색 구성표 (예: 밝은 색 또는 어두운 색)를 나타냅니다. 이렇게 하면 브라우저가 스크롤바, 양식 컨트롤, 기본 캔버스 배경과 같은 네이티브 UI 요소를 사이트 디자인에 맞게 자동으로 테마를 지정하고 초기 로드 중에 흰색 플래시를 최소화할 수 있습니다.
구현
1. HTML에서 지원되는 스키마 선언
필수: '테마가 적용되지 않은 콘텐츠의 깜박임' (FOUC)을 방지하려면 <head>에 <meta> 태그를 배치하여 브라우저가 렌더링을 시작하기 전에 지원하는 테마를 알 수 있도록 합니다. 이 <meta> 태그는 초기 캔버스 색상을 일찍 설정하여 FOUC를 방지하는 데 도움이 되지만, 모든 브라우저 또는 로드 조건에서 깜박임을 완전히 제거하지는 못할 수 있습니다.
<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">
2. CSS :root 또는 html에 페이지 전체 색 구성표 적용
필수: html 요소 또는 :root 가상 클래스에 color-scheme 속성을 적용합니다. 브라우저는 특히 루트 요소를 확인하여 루트 스크롤바와 초기 '캔버스' 배경을 포함한 전체 표시 영역의 테마를 결정합니다. body에만 적용하면 body가 창의 렌더링 컨텍스트를 제어하지 않으므로 이러한 전역 UI 표시 경로가 밝은 모드로 유지될 수 있습니다.
/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}
3. 밝은 색상 토큰과 어두운 색상 토큰 정의
light-dark() 함수를 사용하여 다양한 color-scheme 값에 자동으로 적응하는 색상 토큰을 정의할 수 있습니다.
원시 색상 값을 별도의 맞춤 속성에 보관하는 것이 좋습니다. 이렇게 하면 다양한 방식으로 더 쉽게 결합할 수 있고 필요한 경우 대체 동작을 더 쉽게 수행할 수 있습니다.
accent-color 또는 scrollbar-color와 같은 기본 제공 UI의 색상을 더 세밀하게 제어하기 위해 작성자는 맞춤 속성 또는 light-dark() 함수를 사용하여 자체 동적 색상을 선택적으로 추가할 수 있습니다. 이 함수는 요소의 계산된 color-scheme에 따라 올바른 색상을 자동으로 선택하고 중복 미디어 쿼리가 필요하지 않지만 기본 구현에는 필요하지 않습니다.
:root {
--color-brand-light: oklch(45% 0.23 270);
--color-brand-dark: oklch(85% 0.15 210);
--color-brand-text-light: white;
--color-brand-text-dark: oklch(40% 0.23 270);
--color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
--color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}
button.primary {
/* These automatically adapt to color scheme */
background-color: var(--color-brand);
color: var(--color-brand-text);
}
선택사항: canvas, canvastext, accentcolor(지원 확인), buttonborder 등 사용되는 색 구성표(및 강제 색상과 같은 기타 색상 모드)에 자동으로 적응하는 여러 시스템 색상을 사용할 수 있습니다. 이러한 색상은 일반적으로 특정 기본 브라우저 UI와 정확히 일치해야 하거나 대체/기본값으로 사용해야 하는 매우 구체적인 경우를 제외하고는 너무 제한적이어서 유용하지 않습니다.
선택사항: 컨텍스트에 맞게 색상 쌍 조정
시스템 기본값을 재정의하는 경우에도 prefers-color-scheme 미디어 쿼리를 사용하여 페이지 주변의 브라우저 및 OS 크롬 색상 (또는 페이지가 iframe으로 사용되는 경우 주변 페이지의 색상)을 고려하는 다양한 색상 쌍을 정의하는 것이 유용할 수 있습니다.
예를 들어 시스템 설정이 dark인 경우 약간 더 어두운 밝은 테마를 사용하거나 시스템 설정이 light인 경우 대비가 더 높은 어두운 테마를 사용하여 페이지가 주변 UI에 의해 시각적으로 압도되지 않도록 합니다.
세분화된 브라우저 UI 맞춤설정
color-scheme를 설정하면 브라우저 UI가 사용된 색 구성표에 맞게 조정되지만, 이 경우 웹사이트 디자인과 완벽하게 일치하지 않을 수 있는 OS 기본값 또는 시스템 색상이 사용됩니다.
최신 브라우저는 이를 위해 세분화된 맞춤설정 후크를 여러 개 노출합니다.
최신 브라우저에서 제공하는 맞춤설정 후크를 모두 사용하지 않고 단순히 모양을 맞춤설정하기 위해 네이티브 컨트롤을 다시 구현하지 마세요.
강조 색상 설정
일부 브라우저 UI (예: 선택된 체크박스 또는 슬라이더)는 강조 색상을 사용합니다.
기본적으로 OS 설정으로 확인되지만 accent-color 속성을 사용하여 페이지의 브랜드 색상과 같이 페이지에 더 잘 어울리는 색상으로 설정할 수 있습니다.
html {
accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}
accent-color 사용 시 주의해야 할 문제
accent-color의 브라우저 렌더링은 다양합니다.
체크박스 표시 및 라디오 버튼 점과 같이 일반적으로 강조 색상 배경 위에 그려지는 표시기의 경우 브라우저에서 다른 처리를 적용할 수 있습니다.
- Chromium과 Firefox는 일반적으로 대비되는 표시기 색상을 선택합니다.
- Safari는 밝은 모드에서 흰색 표시기를 유지하거나 어두운 모드에서 검은색 표시기를 유지하면서 그 뒤의 강조 색상을 수정할 수 있습니다. 이 조정이 제공된 모든 색상에 충분한 대비를 제공하지는 않으므로 Safari에서 밝은 색 구성표와 어두운 색 구성표를 모두 포함하여 중요한 조합을 테스트하세요. Safari가 가장 대비가 좋은 표시기 색상을 자동으로 선택한다고 가정하지 마세요.
스크롤바 색상
scrollbar-color를 light-dark()와 함께 사용하여 사용된 색 구성표에 맞게 맞춤 스크롤바 색상을 설정할 수 있습니다.
:root {
--color-scrollbar-track: light-dark(#eee, #222);
--color-scrollbar-thumb: light-dark(#999, #666);
scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}
scrollbar-color 사용 시 주의해야 할 문제
scrollbar-color에 애니메이션이나 전환을 적용하지 마세요. WebKit 버그로 인해scrollbar-color가 변경될 때마다 스크롤바가 깜박입니다.- macOS에서는 기본적으로
scrollbar-color(표준) 및::-webkit-scrollbar(기존) 속성이 무시됩니다. macOS는 네이티브 '오버레이' 스크롤바를 사용하기 때문입니다. macOS에서 렌더링하도록 하려면 맞춤 색상을scrollbar-width(예:thin또는auto)과 페어링해야 합니다(MUST). scrollbar-width가 적용되어 있어도 macOS 오버레이 스크롤바는 기본적으로 트랙 (여백)을 투명하게 렌더링합니다. 디자인에서 MacOS에 표시되는 트랙 배경 색상이 필요한 경우 스크롤 가능한 컨테이너에scrollbar-gutter: stable;를 적용해야 합니다(MUST). 단, 사용자가 스크롤바 위로 마우스를 가져간 후에만 표시됩니다.scrollbar-gutter: stable를 사용해도 MacOS에서 트랙이 투명할 수 있습니다. 엄지손가락은 트랙 색상에 따라 표시되지 않아야 합니다.
추가 맞춤설정
대부분의 브라우저 UI는 다음과 같이 모양을 완전히 맞춤설정할 수 있는 가상 요소를 노출합니다.
::placeholder::spelling-error::grammar-error::selection::search-text::target-text::file-selector-button
이 중 하나에 light-dark() 색상을 사용하여 사용된 색 구성표에 맞게 조정되는 색상을 적용할 수 있습니다.
4. 밝은 모드와 어두운 모드에 따라 다른 이미지
CSS 이미지의 경우 light-dark()를 사용하여 다른 이미지 값을 제공할 수도 있습니다.
중요: light-dark()의 두 값은 동일한 유형이어야 합니다. 색상을 이미지와 결합할 수 없습니다.
둘 중 하나가 이미지여야 하는 경우 image(<color>)를 사용하여 색상 값을 래핑합니다.
JS 기반 테마 감지
대부분의 색 구성표 분기는 CSS에서 실행되어야 하며, 이는 변경사항에 자동으로 적응합니다.
matchMedia("(prefers-color-scheme: dark)").matches를 읽는 모든 JS는 시스템 환경설정이 언제든지 변경될 수 있으므로 change 이벤트도 처리해야 합니다(MUST).
선택사항: 색 구성표 전환 구현
사용자가 이 사이트의 시스템 기본값을 수동으로 재정의하도록 허용하려면 어두운 모드 전환을 구현하면 됩니다. 대부분의 사이트에서는 사이트 UI에 영구 전환 버튼이 필요하지 않습니다. 일반적으로 시스템 색 구성표를 기본값으로 설정하는 것으로 충분하며, 별도의 설정 화면에서 설정할 수도 있습니다. 어두운 모드 전환 (영구 또는 비영구) 구현에 관한 권장사항은 어두운 모드 전환을 참고하세요.
선택사항: 구성요소별 재정의
특정 요소에 color-scheme를 설정하여 전역 테마를 재정의할 수 있습니다.
자세한 내용과 주의사항은 구성요소별 라이트/어두운 테마를 참고하세요.
알려진 문제
색 구성표를 사용할 때 주의해야 할 문제
- Chrome과 Firefox는 iframe의
color-scheme를 준수합니다. 즉, 삽입된 페이지를 올바른 색 구성표로 렌더링하고 삽입된 페이지의prefers-color-scheme미디어 쿼리를 조정하여 삽입 컨텍스트의color-scheme를 반영합니다. Safari는 그렇지 않으며 iframe 내부에서도prefers-color-scheme를 시스템 설정으로 확인합니다.- 상위 요소와 iframe을 모두 제어하는 경우: iframe 구성 시 URL 매개변수 (
?theme=dark)를 통해 또는postMessage()(런타임 변경사항에 반응할 수도 있음)를 통해 상위 요소의 색 구성표를 iframe에 명시적으로 전달합니다. iframe에서prefers-color-scheme에 의존하는 대신 해당 신호의<html>(또는:root의color-scheme)에 클래스를 설정합니다. - 삽입된 페이지만 제어하는 경우: Safari의 iframe 내에서 삽입 컨텍스트의
color-scheme를 감지하는 신뢰할 수 있는 방법이 없습니다. 삽입 API (예: 쿼리 문자열 또는postMessage프로토콜)에 명시적인 테마 매개변수를 노출하고 삽입자를 위해 문서화합니다.
- 상위 요소와 iframe을 모두 제어하는 경우: iframe 구성 시 URL 매개변수 (
대체 전략
color-scheme의 대체 및 브라우저 지원
color-scheme 속성은 점진적 개선입니다.
이를 지원하지 않는 브라우저는 이 속성을 무시하고 기본 밝은 모드 UI를 사용합니다.
이전 브라우저에서 사용자의 환경설정에 맞게 조정하려면 prefers-color-scheme 미디어 쿼리를 사용하여 어두운 모드가 선호될 때 다른 색상을 제공합니다.
- 미디어 쿼리를 사용하여
:root또는html에서 맞춤 속성을 전환합니다. - 구성요소에 색상 외에 매우 구체적인 유형의 어두운 모드 맞춤설정이 필요한 경우가 아니라면 개별 구성요소에 미디어 쿼리를 사용하지 마세요.
:root {
/* Define brand colors for each mode */
--color-brand-light: #0056b3;
--color-brand-dark: #00e5ff;
--color-brand: var(--color-brand-light);
/* MANDATORY: Fallback for browsers without light-dark support */
@media (prefers-color-scheme: dark) {
--color-brand: var(--color-brand-dark);
}
/* Ignored in older browsers */
color-scheme: light dark;
}
button.primary {
background-color: var(--color-brand);
}
light-dark()의 대체 및 브라우저 지원
color-scheme는 지원하지만 아직 light-dark()는 지원하지 않는 브라우저의 경우, 먼저 색상의 밝은 버전과 어두운 버전을 맞춤 속성으로 정의해야 하며, 아래 예와 같이 prefers-color-scheme 미디어 쿼리를 사용하여 각 모드의 색상을 설정해야 합니다.
:root {
/* Define browser UI accent color for each mode */
--brand-accent-light: #0056b3;
--brand-accent-dark: #00e5ff;
--accent-color: var(--brand-accent-light);
/* MANDATORY: Fallback for browsers without light-dark support */
@media (prefers-color-scheme: dark) {
--accent-color: var(--brand-accent-dark);
}
/* OPTIONAL: use light-dark() for more control of built-in UI colors */
@supports (color: light-dark(white, black)) {
--accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
}
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
/* Example inherited color property */
accent-color: var(--accent-color);
}
pre, code {
color-scheme: dark;
/* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
accent-color: var(--accent-color);
}
light-dark() 이미지 값에 대한 대체 및 브라우저 지원
이미지에 light-dark()를 지원하지 않는 브라우저의 경우 image-set()를 스위치로 사용합니다. 각 옵션에 맞춤 속성의 type()를 지정하고 비활성 옵션을 지원되지 않는 MIME 유형으로 설정하여 브라우저가 해당 옵션을 삭제하도록 합니다.
활성 옵션은 빈 값을 가져오므로 type()가 없는 일반 image-set() 옵션입니다.
:root에서 스위치를 한 번 정의합니다. 그러면 모든 사용 사이트에서 light-dark()과 같이 두 이미지를 모두 인라인으로 포함하기만 하면 됩니다.
:root {
--light-type: ;
--dark-type: type("image/do-not-use");
/* MANDATORY: Fallback for browsers without light-dark() image support */
@media (prefers-color-scheme: dark) {
--light-type: type("image/do-not-use");
--dark-type: ;
}
}
.hero {
background-image: image-set(url(hero-light.png) var(--light-type), url(hero-dark.png) var(--dark-type));
@supports (background-image: light-dark(url("a"), url("b"))) {
background-image: light-dark(url(hero-light.png), url(hero-dark.png));
}
}
- 선택한 이미지만 다운로드됩니다.
- 해상도는 여전히 작동합니다.
image-set(url(a-1x.png) 1x var(--light-type), url(a-2x.png) 2x var(--light-type), url(b-1x.png) 1x var(--dark-type), url(b-2x.png) 2x var(--dark-type)) - 활성 옵션에
type("")를 사용하지 마세요. 브라우저는 빈 문자열을 지원되지 않는 것으로 처리하고 모든 옵션을 삭제합니다. light-dark()와 달리 스위치는 하위 트리에 설정된color-scheme를 따르지 않습니다. 수동 전환 또는color-scheme를 설정하는 하위 트리는--light-type및--dark-type도 전환해야 합니다(MUST).
대안: 이미지당 맞춤 속성 1개
몇 개의 이미지만 변경되는 경우에는 더 간단하지만, 모든 이미지에 자체 미디어 쿼리 브랜치가 필요하며 수동 전환으로 각 이미지를 전환해야 합니다.
:root {
--hero-image: url(hero-light.png);
@media (prefers-color-scheme: dark) {
--hero-image: url(hero-dark.png);
}
@supports (background-image: light-dark(url("a"), url("b"))) {
--hero-image: light-dark(url(hero-light.png), url(hero-dark.png));
}
}
.hero {
background-image: var(--hero-image);
}
image()의 대체 및 브라우저 지원
색상 정지가 하나이고 위치가 없는 그라데이션을 사용합니다(예: linear-gradient(blue)).
더 광범위한 브라우저 지원을 위해 단일 색상 중지점에 0 100% 위치를 명시적으로 제공할 수 있습니다(예: linear-gradient(blue 0 100%)).
scrollbar-color의 대체 및 브라우저 지원
이 기능은 점진적 개선이며 항상 대체가 필요한 것은 아닙니다.
스타일 지정이 중요하고 사용자의 기준 타겟이 '기준 널리 사용 가능' 이하인 경우 비표준 ::-webkit-scrollbar 가상 요소를 대체로 포함해야 합니다(SHOULD).
기존 대체 항목을 @supports not (scrollbar-color: auto) 블록으로 래핑하여 두 가지를 모두 기본적으로 지원하는 브라우저에서 표준 속성과 기존 WebKit 선택기 간의 충돌을 방지합니다.
맞춤 속성을 사용하여 색상을 정의하는 경우 기존 WebKit 선택기에 자동으로 적용됩니다. 중복으로 만들 필요는 없습니다.
/* Legacy fallback for WebKit/Blink browsers */
@supports not (scrollbar-color: auto) {
.scroller::-webkit-scrollbar {
/* Must define base size in WebKit for custom colors to be visual */
width: 12px;
height: 12px;
}
.scroller::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
}
.scroller::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
}
accent-color의 대체 및 브라우저 지원
accent-color 속성은 점진적 개선입니다.
이 속성을 지원하지 않는 브라우저는 이를 무시하고 기본 UI 색상을 사용합니다.