3P by GN⁺ 4시간전 | ★ favorite | 댓글 1개
  • 브라우저 기본 기능부터 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.cssdark.css를 각각 prefers-color-scheme: lightprefers-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에서 확인됨
    • 검은 종이와 흰 잉크를 쓰는 프린터가 있다면 아쉽겠다는 농담성 언급 포함
Hacker News 의견들
  • 커스텀을 많이 한다면 전용 파일이 말은 되지만, 미디어 쿼리에 안 맞는 CSS가 다운로드조차 안 된다는 설명은 실제 브라우저 동작과 다르다고 봄. 내 경험상 브라우저는 결국 전부 다운로드하고, 우선순위만 다르게 줄 뿐임
  • 서버에서 초기 콘텐츠를 기다리는 동안 생기는 플래시뱅 같은 번쩍임을 막을 방법이 아직 없는지 궁금했음
    • 나는 Firefox의 userContent.css에서 background-color를 지정하는 방식이 괜찮다고 봄
    • 나는 그냥 화면 밝기를 낮추고 다크 모드를 끄면 플래시뱅이 없어졌음. 덤으로 배터리도 더 오래감
  • 나는 이 글이 다크 모드 배경의 검정 농도 취향 얘기일 줄 알았음. 순수한 검정이 OLED에서 배터리 효율이 더 좋다는 말도 들었고, 완전한 검정보다 잉크 덜한 회색을 더 좋아하는 사람도 알고 있음. 다만 굳이 여섯 단계까지 필요한지는 잘 모르겠고, 체감 가능한 건 많아야 3~4단계쯤이라고 느꼈음
    • 나는 더 보편적인 해법이 Reader Mode 호환성 표준화라고 생각했음. 각 사이트가 모든 사용자 취향을 일일이 맞추는 n x m 문제 대신, 사이트는 하나의 단순한 콘텐츠 뷰만 지원하고 브라우저가 그 위에서 사용자별 설정을 맡는 구조가 더 낫다고 봄
    • 나는 OLED에서는 순수한 검정을 선호하는 편임. 픽셀이 덜 켜질수록 번인 부담이 줄어든다고 느끼고, 어차피 수명이 한정돼 있으니 장기적으로는 모니터를 2~3년보다 5년 이상 쓰고 싶다는 쪽임
  • 내 기준 최고 레벨은 9, 아니면 0인데, 그냥 컴퓨터를 끄고 가서 잠을 자는 것
  • 나는 OP가 3상태 토글을 제대로 구현한 점이 반가웠음
  • 나는 스크롤을 내려가면서 단계가 동적으로 적용됐으면 더 재밌었겠다고 생각했음
    • 아니면 페이지의 적절한 위치마다 독자가 직접 단계 선택을 할 수 있어도 괜찮겠다고 봤음
  • 내가 보기엔 8단계 아닌가 싶었음
  • 지금이 2024년이라는 느낌이었음
  • 이 상황에서 떠오르는 건 역시 xkcd 3227였음