목차

이 글은 제가 어두운 테마를 완성시키기 위한 과정, 그리고 그 안에서 만난 애로사항들과 해결법을 주제로 합니다.

간략 설명

저는 이러한 방식으로 만들었습니다.

  1. CSS 상의 모든 색깔을 변수로 대체한다.
  2. 테마를 인식하고 <body> 태그에 명시할 스크립트를 작성한다.
  3. 명시한 속성값에 따라 CSS의 색깔 변수가 변하게 한다.
  4. 테마를 바꾸는 버튼을 만든다.
복붙만 하고 싶은 분들을 위한 간단 통합본
  1. <head> 안에 복붙

    <script type='text/javascript'>
      function themeChange() {
        if( document.body.getAttribute('data-theme') == 'light' ) {
          document.body.setAttribute('data-theme', 'dark');
          window.localStorage.setItem('theme','dark');
        } else if( document.body.getAttribute('cm-theme') == 'dark' ) {
          document.body.setAttribute('data-theme', 'light');
          window.localStorage.setItem('theme', 'light');
        }
      }
    </script>
  2. <body> 안 가장 위에 복붙

    <script type='text/javascript'>
      if( window.localStorage.getItem('theme') ) {
        document.body.setAttribute('data-theme', window.localStorage.getItem('theme'));
      } else if( window.matchMedia('(prefers-color-scheme: dark)').matches ) {
        document.body.setAttribute('data-theme','dark');
      } else {
        document.body.setAttribute('data-theme','light');
      }
    </script>
  3. CSS 설정
  4. 버튼 추가

    <button onclick='themeChange();'></button>

CSS

CSS의 색상값을 변수로

여러가지 색깔을 일괄적으로 변환시키기 위해서는 색상값들을 변수로 대체하는 것이 편합니다.
물론 필수는 아니지만, 향후 유지보수 측면에서도 변수로 관리하는 것이 편할 것입니다.

작업 전에, 다크 모드로 전환 시에 바뀔 색깔들을 분류해보시는 것이 좋습니다.
가장 쉬운 방법은 다크 모드에서의 색상들을 임의로 골라보시고, 같은 색에서 같은 색으로 변하는 부분끼리 모아보는 것입니다.
아마 비슷한 역할의 부분들끼리 자연스레 모일 것으로 예상됩니다.

CSS에 :root{}를 만들어 변수를 지정할 겁니다.
최상위 요소가 :root인거지, html{}이나 body{}도 가능합니다. 취향이나 상황에 맞는 것으로 고르시면 됩니다. 왼쪽에서부터 순서대로 적용 범위가 작아집니다.

우선 예시를 보여드리겠습니다.

:root {
  --background: white;
}

이렇게 하면 '--background'에 'white'이 저장됩니다. 여기서 '--background'는 변수 이름이고, 'white'은 저장되는 값입니다.
변수 이름은 무조건 '--'(대시 두 개)로 시작해야 합니다.

이제 교체할 색상값을 변수로 교체합니다.

body {
  background-color: var(--background);
}

위와 같이 색상값 대신 var()안에 변수 이름을 넣으면 위에서 저장한 값이 됩니다. 'background-color: white;'과 동일한 결과를 보여줍니다.

동일한 방법으로 바뀌게 할 모든 색상값을 변수로 교체해주세요.

다크 모드일 때 색상 설정

뒤에 나올 스크립트를 이용해 저희는 <body>data-theme 속성을 추가할 것입니다.
이것을 이용해서 다크 모드일 때의 색상을 입력해보겠습니다.

body[data-theme="dark"] {
  --background: black;
}

위와 같은 형태입니다.
속성 선택자를 이용해서 'data-theme'의 값이 'dark'일 때만 아래의 색깔을 적용합니다.

위치는 꼭 :root{} 다음이어야 합니다.
혹은 :root 대신 body:not([data-theme="dark"])를 사용해서 완전 분리해버릴 수도 있습니다.

스크립트

테마 인식

우리에게는 세 가지 상황이 있습니다.

  • 사용자가 한 번이라도 버튼을 눌렀을 경우
  • 사용자가 버튼을 누른 적이 없고, 선호하는 테마를 설정한 경우
  • 사용자가 버튼을 누른 적이 없고, 선호하는 테마를 설정하지 않은 경우

들어가기에 앞서, 버튼에서 등장할 한 기능에 대해서 설명드리겠습니다.

window.localStorage.setItem('theme','dark');

이 코드는 브라우저에 정보를 저장해놓습니다.
만약 버튼을 눌렀다면, 선호하는 테마를 결정한 것이나 다름이 없으니 그것을 기억해 놓으려는 의도입니다.
방식은 CSS 때와 비슷합니다.
'theme'은 변수 이름이고 'dark'는 저장값으로 생각하시면 됩니다. 물론 그 이름과 값들은 마음대로 하셔도 됩니다.

다시 위의 세 가지 상황으로 돌아가면, 이것을 이용해 '사용자가 한 번이라도 버튼을 눌렀을 경우'를 판단할 수 있습니다.

if( window.localStorage.getItem('theme') );

원래라면 'window.locaStorage.getItem('theme')'은 위에서 저장했던 'dark'라는 값을 얻기 위해 사용하는 구문입니다.
이것을 if의 조건으로 사용해서 이 값의 존재 여부를 판단합니다.

두 번째 조건문입니다.

if( window.matchMedia('(prefers-color-scheme: dark)').matches )

이건 사용자가 브라우저 설정을 어두운 테마로 설정했는지 확인합니다.
사용자가 밝은 테마로 설정한 경우와 아예 설정하지 않은 경우는 결과가 같아도 됩니다. 기본이 밝은 테마니까요.

각 상황별로 맞는 속성값을 <body>에 추가합니다. setAttribute()를 이용해서요.

이제까지의 모든 구문을 종합하면 이렇습니다.

if( window.localStorage.getItem('theme') ) {
    document.body.setAttribute('data-theme', window.localStorage.getItem('theme'));
  } else if( window.matchMedia('(prefers-color-scheme: dark)').matches ) {
    document.body.setAttribute('data-theme','dark');
  } else {
    document.body.setAttribute('data-theme','light');
  }

깜빡임 문제

그런데 이것을 그냥 <head><script>로 넣으면 작동이 안되고, 함수로 만들어 onload를 사용하거나, <script type='module'>을 사용하거나 하면 깜빡임이 발생합니다.
스크립트를 기다리지 않고 본문부터 출력해서 그렇습니다.
이것을 해결하기 위해서는 <script><body> 안에 넣어야 합니다.
<body> 안 가장 위에 넣으면 내용이 로드되기 전에 스크립트가 실행되면서 깜빡임이 사라집니다.

테마 전환 기능

이제 테마를 전환하는 함수입니다. 버튼의 기능이기도 하죠.

매커니즘은 단순합니다.
현재 어떤 테마인지 보고, <body>의 'data-theme' 속성을 반대로 바꾸고, 브라우저에 그 값을 저장합니다.

이걸 전부 합하면 이런 형태가 됩니다.

function themeChange() {
  if( document.body.getAttribute('data-theme') == 'light' ) {
    document.body.setAttribute('data-theme', 'dark');
    window.localStorage.setItem('theme','dark');
  } else if( document.body.getAttribute('data-theme') == 'dark' ) {
    document.body.setAttribute('data-theme', 'light');
    window.localStorage.setItem('theme', 'light');
  }
}

이것을 외부 스크립트나 스크립트 태그로 넣어주세요.

테마 바꾸는 버튼

이건 간단합니다.

<button onclick='themeChange();'></button>

버튼만 추가해주면 끝입니다.

완전히 흰색이나 완전히 검은색은 좋지 않다고 합니다. 무작정 어두운 색을 사용한다는 느낌보다는 채도가 낮은, 눈이 편한 색을 사용한다는 느낌인 것 같아요.