브라우저 기본 기능부터 JavaScript 미디어 쿼리 까지, 다크 모드 구현 범위를 점진적으로 넓혀 가는 8단계 구분 정리
가장 단순한 방식은 <meta name="color-scheme" content="light dark"> 또는 color-scheme: light dark 선언만으로 사용자 색상 스킴 선호 를 따르게 하는 구성
더 높은 단계에서는 light-dark() 함수, @media (prefers-color-scheme: dark), 스킴별 분리 스타일시트 로 색상뿐 아니라 이미지와 그림자까지 폭넓은 조정 가능
사용자 시스템 설정만 따르지 않고 Automatic·light·dark 3가지 선택지를 제공하는 전환기 구성 가능하며, :has()와 실제 meta 요소 를 기준으로 테마 판별 가능
Safari의 접근성 한계 와 인쇄 시 prefers-color-scheme 동작 관찰까지 포함되며, 최근 CSS 기능만으로도 라이트·다크 모드 내장이 쉬워졌음이 드러남
다크 모드 단계별 구현
Level 1: Barebone
CSS 한 줄 없이도 light/dark 구분 활성화 가능하며, 문서 head에 <meta name="color-scheme" content="light dark"> 추가만으로 브라우저가 사용자 색상 스킴 선호 를 따르기 시작함
content 속성의 항목 순서는 이론상 의미가 있으며, 색상 스킴 선호를 지정하지 않은 사용자는 공백으로 구분된 목록의 첫 번째 값을 받음
현재 운영체제 설정에는 색상 스킴을 선택하지 않는 옵션이 없어, 실제로는 운영체제 설정과 일치하는 스킴으로 귀결됨
content에 단일 값만 지정할 수도 있으며, 이 경우 사용자 선호를 고려하지 않고 해당 스킴을 강제 적용함
이 메타 태그는 어느 정도 다음 단계의 CSS 방식과 대응하는 HTML 측 방식 역할 수행
Level 2: Basic
CSS에서는 html { color-scheme: light dark; } 선언으로 라이트/다크 모드 구분 적용 가능
이미 DOM에 메타 태그가 있다면 이 선언은 필요하지 않으며, HTML을 제어할 수 있다면 브라우저가 CSS 파싱 전부터 지시를 알 수 있는 메타 태그 사용 권장
두 방식 모두 사용자 에이전트 기본 스타일과 그에 포함된 라이트/다크 모드 를 활용할 수 있게 해줌
여기에 CSS를 추가하되 CSS system colors 사용 위주로 제한하면 꽤 정돈된 디자인 구현 가능
메타 태그가 항상 문서 전체에 적용되는 것과 달리, CSS color-scheme 선언은 루트 요소 외 다른 위치에도 설정 가능하며, 이 차이로 추가 활용 여지 존재
Level 3: Benign
비교적 최근 추가된 CSS의 light-dark() 색상 함수 로 단순한 라이트/다크 모드 조정 가능
예시에서는 background-color: light-dark(black, white);와 color: light-dark(white, black);처럼 사용하며, 첫 번째 인수는 라이트 모드에 적용되고 두 번째 인수는 다크 모드에 적용됨
인수에는 실제 색상을 직접 넣을 수도 있고, 색상으로 해석되는 custom properties 를 넣을 수도 있음
이 글 전체에서 이 단계만 작성 시점 기준 브라우저 지원 이 충분하지 않음
Level 4: Bold
전통적인 @media (prefers-color-scheme: dark) 미디어 쿼리 로 다크 모드 전환 구현 가능
light 또는 dark 어느 쪽을 질의하든, 단순 색상 변경에 한정되지 않는 최대 수준의 커스터마이징 가능
다크 모드에서 이미지를 필터로 저채도 처리하거나, 박스 그림자를 외곽선으로 대체하는 식의 조정도 모두 가능
Level 5: Bisectional
미디어 쿼리는 HTML에서도 사용할 수 있으며, link 요소의 media 속성에 넣어 스킴별 스타일시트 분리 가능
예시로 light.css와 dark.css를 각각 prefers-color-scheme: light와 prefers-color-scheme: dark에 연결하는 방식 제시
커스터마이징 범위가 큰 경우 전용 파일 구성이 적합하며, 브라우저는 질의와 맞지 않는 CSS 파일을 무시할 수 있어 다운로드 대상이 하나 줄어들 수 있음
Level 6: Ballistic
JavaScript에서는 window.matchMedia('(prefers-color-scheme:dark)')로 색상 스킴 미디어 쿼리 사용 가능
다른 미디어 쿼리와 같은 방식으로 라이트 또는 다크 스킴 여부를 질의한 뒤, 그 결과를 바탕으로 원하는 처리 수행 가능
실제 구현에서는 앞선 단계들의 기법을 한 가지만 고수하지 않고 혼합 적용 가능
사용자 전환기와 고급 패턴
Level 7: Beyond
사용자 시스템 선호에만 의존할 필요 없이 color scheme switcher 구축 가능
이 전환기는 단순 불리언이 아니며, 초기 기본값으로 prefers-color-scheme를 따르는 Automatic 모드 필요
그 위에 전환기를 얹으면 사용자는 Automatic, light, dark의 세 가지 모드 중 하나 선택 가능
Level 8: Beguiling
Level 7 전환기 구현 시 일반적으로 HTML 요소에 .dark 클래스나 data-theme="dark" 같은 속성을 추가하는 방식이 흔함
대신 :has()를 사용해 실제 <meta name="color-scheme" content="dark"> 존재 여부를 직접 질의 가능
예시에서는 html:has(meta[name="color-scheme"][content="dark"]) 선택자 아래에서 --color-bg, --color-text 같은 CSS 변수 를 다크 모드 값으로 설정
별도 클래스나 데이터 속성 없이도 실제 meta 요소 를 기준으로 테마 판별 가능
추가 논의와 관찰
CSS Naked Day 관찰
스타일 제거 후 방문한 거의 모든 사이트에서 다크 모드 부재 가 눈에 띄었고, 이것이 다크 모드 단계 구분으로 이어짐
새 사이트를 처음부터 구축하면서 새 스타일을 작성할 경우, 최근의 CSS 기능으로 라이트/다크 모드 내장 이 매우 쉬워졌다는 언급 포함
Safari 접근성 이슈
비교적 최근까지 Safari는 다크 모드에서 접근 가능한 링크 색상 을 제공하지 않았다는 지적 포함
이전 CSS Naked Day에는 이 문제를 발견하고 메타 태그를 제거한 뒤 라이트 색상 스킴만 사용한 경험 언급
이후 다시 메타 태그를 추가했지만, 구버전 Safari 사용자에게는 접근성 저하 가 생길 수 있음을 인지
Safari의 다크 모드에서 텍스트 상자에 보이는 테두리 부재 도 확인됨
사용자 에이전트 스타일만으로는 올바른 시맨틱 HTML을 사용해도 완전한 접근성을 보장하지 못해, 향후 CSS Naked Day에도 충분한 스타일을 유지하는 방안 고민
인쇄와 screen and 조건
Bisectional 예시에서 screen and ...를 사용한 이유로 프린터 대상 제외 의도 언급
테마 비의존 코어 스타일시트나 전용 인쇄 스타일시트가 따로 있다고 가정하고, 프린터에서 다크 모드가 잉크를 많이 쓸 수 있어 안전하게 분리하려는 판단 제시
실제 테스트에서는 시스템 다크 모드를 켠 상태로 인쇄해도 검은 텍스트와 흰 종이만 출력되었고, 브라우저들이 인쇄에 해당 다크 모드 스타일을 적용하지 않는 것으로 관찰됨
추가 테스트에서 인쇄 미리보기에서는 prefers-color-scheme가 항상 light 로 보고되었으며, Firefox와 Chromium에서 확인됨
검은 종이와 흰 잉크를 쓰는 프린터가 있다면 아쉽겠다는 농담성 언급 포함