구성요소별 밝은 테마/어두운 테마

color-scheme 속성은 루트에 설정하는 것이 더 일반적이지만 개별 요소에 설정하여 페이지의 나머지 부분과 다른 색 구성표를 적용할 수도 있습니다. 이는 항상 특정 색 구성표 (예: 항상 어두운 모드 또는 밝은 모드)로 표시되어야 하는 구성요소에 유용할 수 있습니다.

사용 사례의 예를 들면 다음과 같습니다.

  • 미적 이유로 밝은 모드 페이지에서도 어두운 모드로 표시되는 요소(예: 코드 블록, 미디어 플레이어, 사진 갤러리)
  • 밝은 배경에 맞게 디자인된 미디어 (예: 이미지, 동영상, 삽화, 인쇄 미리보기)가 포함된 영역은 페이지의 나머지 부분이 어두운 모드인 경우에도 밝은 모드로 설정할 수 있습니다.
  • 구성요소 프리뷰와 같이 사용자 수준 설정으로 색 구성표가 제어되는 요소
  • 밝은 모드와 어두운 모드를 모두 지원하지 않는 삽입
  • 디자인 도구, 지도, 시각화, 게임 등

색상을 변경해야 하는 경우와 color-scheme를 강제해야 하는 경우

밝은 모드에서 어두운 배경에 밝은 텍스트를 사용하거나 어두운 모드에서 밝은 배경에 어두운 텍스트를 사용하는 모든 요소에 다른 color-scheme가 필요한 것은 아닙니다. 예를 들어 기본 버튼은 밝은 모드에서 흰색 텍스트가 있는 파란색으로 렌더링될 수 있지만 color-scheme: dark를 보장하지는 않습니다.

일반적으로 다른 color-scheme를 사용하는 요소는 단순하고 얕은 컨테이너가 아니라 자체 시각적 컨텍스트를 설정하는 복잡한 표면입니다.

요소에 다른 color-scheme를 사용하는 것을 고려할 때는 다음 질문을 스스로에게 던져 보세요.

  • 달리 맞춤설정되지 않은 기본 제공 브라우저 UI (예: 양식 컨트롤, 스크롤바 등)가 해당 색 구성표를 사용해야 하나요, 아니면 페이지의 색 구성표에 맞게 조정해야 하나요? -> 전자의 경우 color-scheme를 사용하지 마세요.
  • light-dark() 색상이 페이지의 나머지 부분과 같이 또는 재정의에 따라 확인되어야 하나요? -> 전자의 경우 color-scheme를 사용하지 마세요.
  • 하위 요소가 color-scheme에 있어야 하나요? 그렇지 않은 경우 color-scheme를 사용하지 마세요.

기본 구현

구성요소별 재정의는 일반적으로 (엄격하게는 아니지만) 전역 color-scheme를 통해 여러 색 구성표를 지원하는 페이지에서 사용됩니다. 페이지 전체에 어두운 모드를 올바르게 구현하려면 어두운 모드를 참고하세요.

페이지 전체 color-scheme가 설정되고 이에 민감한 색상 토큰을 사용하는 경우 (예: light-dark()를 통해) 특정 구성요소에 color-scheme를 설정하여 해당 하위 트리의 색상 모드를 재정의할 수 있습니다.

pre, code, .dark {
  color-scheme: dark;
}

일부 브라우저는 구성요소를 다른 색 구성표에 자동으로 조정합니다. 모든 경우에 지정된 색 구성표를 강제 적용하려면 only(예: color-scheme: dark; 대신 color-scheme: only dark;)를 사용합니다.

색상이 아닌 값 조정

light-dark()는 현재 색상에만 작동합니다.

권장사항

  • 필수: 배경이 없는 요소에는 color-scheme를 설정하지 마세요. 다른 색상 구성표의 배경과 텍스트 색상 쌍이 혼합되어 텍스트를 읽을 수 없게 될 수 있습니다.
  • 선택사항: 페이지 전체 어두운 모드와 동일한 색상 쌍을 재사용하는 것이 더 쉽지만 이러한 구성요소에 다른 색상 쌍을 정의할 수 있습니다. 예를 들어 밝은 모드 페이지에서 사용되는 어두운 모드 구성요소는 전체적으로 어두운 모드인 페이지에서 동일한 어두운 모드 구성요소가 사용되는 경우보다 약간 덜 어둡게 표시할 수 있습니다.

알려진 문제

중요한 주의사항: light-dark() 색상 상속

light-dark()는 계산된 값 시간에 확인됩니다. 즉, light-dark() 색상으로 설정된 상속된 <color> 속성은 light-dark() 표현식 자체가 아닌 두 색상 중 하나만 하위 요소에 전달합니다.

여기에는 다음이 포함됩니다.

  • 상속되는 기본 제공 색상 속성(예: color, accent-color, fill, stroke, text-shadow, caret-color)
  • syntax: <color> 및 inherits: true가 있는 등록된 상속 가능한 맞춤 속성
  • inherit로 설정된 다른 <color> 속성

즉, 다음을 수행해야 합니다.

  • 디자인 토큰 (예: --surface-color)을 보유하기 위한 맞춤 속성을 <color>로 등록하지 않습니다. 하위 요소가 다른 color-scheme에서 토큰을 다시 확인할 수 있도록 토큰은 light-dark() 표현식을 활성 상태로 유지해야 합니다.
  • 요소에 color-scheme를 설정할 때 light-dark() 값으로 설정되었을 수 있는 상속된 <color> 속성을 직접 또는 디자인 토큰을 통해 다시 지정합니다. 동일한 디자인 토큰에 지정하는 경우에도 마찬가지입니다.
  • color-scheme 재정의가 있는 요소의 <color> 속성에 inherit를 사용하지 않습니다(하위 요소에 사용하는 것은 괜찮음).
  • 반대 사용 사례의 경우 등록된 <color> 속성을 사용합니다. 즉, 상위 요소의 확인된 색상을 의도적으로 스냅샷으로 만들고 하위 요소의 color-scheme에서 다시 확인되지 않도록 하려는 경우입니다. 예를 들어 다른 곳에서 사용할 페이지 배경을 캡처하는 경우입니다.
  • 색상에 애니메이션을 적용해야 하는 경우 애니메이션이 적용되는 요소에 별도의 @property 등록 <color> 속성을 사용합니다 (색상 보간을 위해 등록이 필요함). 이는 디자인 토큰이 아니라 요소별 애니메이션 타겟이므로 위의 규칙과 충돌하지 않습니다.

예:

:root {
    --accent-color: light-dark(blue, skyblue);
    --surface-color: light-dark(white, #222);
    --text-color: light-dark(#111, white);

    color-scheme: light dark;
    accent-color: var(--accent-color);
    color: var(--text-color);
}

body {
    /* --surface-color dynamically switches despite being inherited because --surface-color is not registered */
    background: var(--surface-color);
}

pre, code {
    color-scheme: dark;
    background: var(--surface-color);

    /* Without this, accent-color would be blue, not skyblue! */
    accent-color: var(--accent-color);

    /* Without this, text-color would be #111, not white! */
    color: var(--text-color);
}

색 구성표를 사용할 때 주의해야 할 문제

  • 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 프로토콜)에 명시적인 테마 매개변수를 노출하고 삽입자를 위해 문서화합니다.

대체 전략

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