본문으로 바로가기
  1. Home
  2. 그외/유용한 정보
  3. [JAVASCRIPT] 웹사이트에 토글로 다크모드를 적용하는 방법과 예제

[JAVASCRIPT] 웹사이트에 토글로 다크모드를 적용하는 방법과 예제

· · 쓰윔

 여러 메이저 웹사이트에서 다크모드를 도입하고 있습니다.

 이제 다크모드는 웹 사이트에서 하나의 기본 소양처럼 되버린 느낌입니다.

 아래는 유튜브에서 제공하는 다크모드의 모습인데, 유튜브 뿐 아니라 웬만한 메이저 사이트들은 자체적으로 다크모드를 제공하고 있습니다.

다크모드에는 다음과 같은 장점이 있습니다.

 

  • 쨍한 느낌에 지친 눈의 피로감을 줄여줄 수 있습니다.
  • 사용자가 스마트폰을 사용시, 배터리 사용량을 줄여줍니다.

 

구현할 다크모드의 원리와 예제

 

 먼저 완성한 모습부터 보여드리겠습니다.

 아래에 있는 버튼을 누르면 글씨들의 색깔과 배경이 변하는 것을 볼 수 있습니다.

 원리를 요약하면 다음과 같습니다.

 다크 모드를 구현하는 데 핵심적인 것은 CSS 변수입니다.

 이 CSS 변수를 설명하자면 아래와 같습니다.

 

:root {
--font-color: #000;
}

.darkmode {
--font-color: #fff;
}

p {color:var(--font-color)}

/*기본 상태라면 --font-color는 #000이란 값을 가집니다. .darkmode 클래스가 붙은 상황이면, --font-color는 #fff란 값을 가집니다.*/

/*선언은 --변수명으로 구현되며, 사용은 var(변수명)으로 사용할 수 있습니다.*/

 

 어떤 클래스가 붙었느냐에 따라 --font-color의 변수값이 #000이 되기도, #fff이 되기도 합니다.

 즉 버튼을 눌러서 body나 html에 원하는 클래스를 달아준다면, 다크모드에서 지정한 색과 평소 상태에서 지정한 색이 지정된다는 뜻입니다.

 

뼈대 잡기

 먼저 다크모드를 적용할 웹사이트를 관찰해야 합니다.

 그리고 웹사이트에서 기본 색상과 다크모드 색상을 입혀줄 대상을 모두 정리해야 합니다.

 예를 들면 이렇습니다.

:root {
  --bg-color:#fff;
  --font-color:rgba(0,0,0,.7);
  --h2-color:rgba(0,0,0,1);
  --kbd-color:orange;
  --kbd-border: orange;
  --check-color: #749ed7;
}
.darkmode {
  --bg-color:#999;
  --font-color:rgba(255,255,255,.7);
  --h2-color:rgba(255,255,255,1);
  --kbd-color: red;
  --kbd-border: red;
  --check-color: #006cff;
}

body {
  background:var(--bg-color);
}
p {
  color:var(--font-color);
}
h2 {
  color:var(--h2-color);
}
kbd {
  color: var(--kbd-color);
  border:1px solid var(--kbd-border);
}

 배경색을 결정해줄 bg-color 변수, 글씨색을 결정해줄 font-color 변수, H2 태그의 색을 정해줄 h2-color 변수 등을 설정했습니다.

 실제 웹사이트라면 방대한 양의 변수가 필요할지도 모릅니다.

 

토글 버튼 만들기

 저는 input checkbox를 이용해서 토글 버튼을 만들었습니다.

 디자인의 경우 코드펜에서 선호하는 디자인을 가져왔습니다.

 

 각각 HTML 구조와 CSS 구조는 다음과 같습니다.

<input type="checkbox" id="dn">
  <label for="dn" class="toggle2">
    <span class="toggle__handler">   
    </span>
 </label>
#dn {
    display: none;
}
.toggle2 {
    cursor: pointer;
    display: inline-block;
    position: relative;
    margin-left: 75px;
    width: 56px;
    height: 32px;
    background: #dfe2e3;
    border-radius: 84px;
    transition: background 200ms cubic-bezier(.445,.05,.55,.95);
}
.toggle2:before,.toggle2:after {
  margin-top:5px;
}
.toggle2:before {
    content: "라이트모드";
    position: absolute;
    left: -85px;
    color: #749ed7;
}
.toggle2:after {
    content: "다크모드";
    position: absolute;
    right: -75px;
}
.toggle__handler {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: inline-block;
    position: relative;
    z-index: 2;
    background: #ffe5b5;
    background: #ffde7b;
    border-color: #debd56;
    box-shadow: none;
    background-size: 0;
    transition: transform .5s;
    border: 0.188em solid #caba94;
}
#dn:checked+.toggle2{
background:#dfe2e3
}
#dn:checked+.toggle2:before{
color:var(--font-color)
}
#dn:checked+.toggle2:after{
  color:var(--check-color)
}
#dn:checked+.toggle2 .toggle__handler{
  width:26px;
  height:26px;
  border-radius:50%;
  display:inline-block;
  transform:translate3d(28px,0,0) rotate(0);
  border:.188em solid #caba94;
  background:#ffefb5;
  box-shadow:inset -.15em -.15em #ffe096;
  background-image:radial-gradient(circle at 60% 10%,#e6cea5 15%,transparent 0),radial-gradient(circle at 30% 50%,#e6cea5 10%,transparent 0),radial-gradient(circle at 60% 70%,#e6cea5 13%,transparent 0);
  transition:transform .2s ease-in-out,background .3s,border-color .3s}

 

 이 CSS와 HTML을 사용하면 토글 버튼을 완성할 수 있습니다. 

 

버튼을 누르면 BODY에 클래스를 걸어주기

 이제 자바스크립트를 도입하면 됩니다.

 코드는 다음과 같습니다.

 주석만 봐도 어떤 의도로 짜여진 코드인지 아실 수 있을 겁니다.

function darkmodeGo () {
  const darkModeToggle = document.getElementById('dn'); // 체크박스 정의
  if (!darkModeToggle) {return !1} // 체크 박스 없을 시 작동 종료
  const Realbody = document.querySelector('body');
  darkModeToggle.addEventListener('change', function(event) {//체크박스의 변화 감지 리스너
    if (!Realbody.classList.contains('darkmode')) { // 바디에 다크모드 클래스가 없으면
      Realbody.classList.add('darkmode'); // 다크모드 추가
 }
    else { // 바디에 다크모드 클래스가 있으면
      Realbody.classList.remove('darkmode'); // 다크모드 클래스를 제거
    }
  })
}
darkmodeGo ()

 체크 박스가 작동했을 때, body에 darkmode 클래스가 걸려 있다면 라이트모드로 바꾼 것이므로 body에서 darkmode 클래스를 제거함으로써 기본 모드로 돌아옵니다.

 

 체크 박스를 작동했을 때, body에 darkmode 클래스가 없다면 다크 모드로 바꾼 것이므로 body에 darkmode클래스를 추가해줌으로써 다크 모드로 갑니다.

 

 이렇게 되면 우선 절반의 완성이라고 볼 수 있습니다.

 왜 절반의 완성이냐면, 사이트에서 사용자가 다크모드를 눌렀는지의 여부를 저장해주지 않았기 때문입니다.

 

 만약 다크모드로 바꿨는데 페이지를 옮겨갈 때마다 다크모드가 풀린다면 사용자 입장에서 굉장히 맥빠지고 불편할 겁니다.

 여기서 해결책이 바로 자바스크립트의 [**localstorage**]입니다.

 

다크모드인지, 라이트모드인지 LocalStorage에 저장하기

 LocalStorage는 브라우저에 값을 저장해주는 저장소입니다.

 즉 다크모드를 설정했는지 여부를 브라우저에 저장해서 불러와주기만 한다면, 이용자가 마지막에 라이트 모드를 선택했는지 다크 모드를 선택했는지를 알 수 있습니다.

function darkmodeGo () {
  const darkModeToggle = document.getElementById('dn'); // 체크박스 정의
  if (!darkModeToggle) {return !1} // 체크 박스 없을 시 작동 종료
  const Realbody = document.querySelector('body');
  darkModeToggle.addEventListener('change', function(event) {//체크박스의 변화 감지 리스너
    if (!Realbody.classList.contains('darkmode')) { // 바디에 다크모드 클래스가 없으면
      Realbody.classList.add('darkmode'); // 다크모드 추가
      localStorage.setItem('whatMode', darkModeToggle.checked); //whatMode라는 이름의 아이템에 체크박스의 체크 여부를 저장하기     
 }
    else { // 바디에 다크모드 클래스가 있으면
      Realbody.classList.remove('darkmode'); // 다크모드 클래스를 제거
      localStorage.setItem('whatMode', darkModeToggle.checked); //whatMode라는 이름의 아이템에 체크박스의 체크 여부를 저장하기     
    }
  })
}
darkmodeGo ()

 아까 소개한 코드에서 경우의 수마다 딱 한 줄의 코드가 더 추가 되었습니다.

 localStorage.setItem으로 시작하는 코드지요.

 whatMode라는 이름으로 체크 유무를 저장했습니다.

 

 이제 저장한 whatMode라는 아이템을 읽어들여서, 새로 페이지가 로딩되면 마지막에 눌렀던 모드가 무엇인지에 따라 다른 동작을 하게 하면 됩니다.

 코드로 나타내면 이렇습니다.

document.addEventListener('DOMContentLoaded',function(){
  const Realbody = document.querySelector('body');
const whatMode = localStorage.getItem('whatMode'); //whatMode 아이템 값 불러오기

 if (whatMode === "false") { // 체크 여부가 false라면, 라이트모드입니다. 이 때 false는 문자열 타입이므로 "" 안에 적어야 합니다.
   return !1; // 라이트모드이므로 아무런 행동을 할 필요가 없습니다.
 }  
  else { // 다크모드라면 
    const darkModeToggle = document.getElementById('dn'); //체크박스를 획득
    darkModeToggle.checked = true; // 체크박스에 체크를 해주기
    Realbody.classList.add('darkmode'); // 다크모드를 body에 걸어주기
  }
})

 이제 끝입니다.

 다크모드의 뼈대가 모두 완성 되었고, 원하는 방향으로 세팅하시면 될 듯 합니다.

 

 마지막으로 위 코드로 실제로 완성한 예제를 올리면서 마치겠습니다.

 다크모드 쪽으로 토글한 후 F5로 새로 고침해도 유지되는 모습을 볼 수 있을 겁니다.

 

 

기타 과정

 이 글에선 다루지 않은 3가지의 과정이 더 있습니다.

 사용자 OS의 다크모드 여부를 감지해주는 CSS - prefers-color-scheme: dark가 첫 번째입니다.

 css의 .darkmode 부분 앞에 @media (prefers-color-scheme: dark)로 감싸주면 OS가 다크모드일 때 자동으로 이 media 쿼리 안에 있는 css가 적용 됩니다.

 

 다만 개인적으로는 사용자가 OS를 다크모드로 쓰고 있다고 해서 웹페이지까지 다크모드로 원한다는 보장은 없다는 생각에 제외했습니다. 

 좀 번거롭더라도, 직접 사용자가 버튼을 눌러 선택하도록 하는게 옳다고 느꼈기 때문입니다.

 

 두 번째로는 라이트 모드/다크 모드의 2가지 경우의 수가 아닌, 여러 테마 색깔을 사용했을 때의 케이스입니다.

 그럴 땐 checkbox가 아니라 radio를 이용해서 여러 경우의 수를 가진 버튼으로 만든 후, 자바스크립트에서 여러 경우의 수를 if else로 만들거나 switch 문을 쓰면 된다고 생각합니다.

 

 마지막으로 인터넷 익스플로러(이하 IE)에서 CSS 변수 기능이 작동하지 않았을 때의 대응법입니다.

 폴리필을 쓰는 방법도 있겠지만, 개인적으로는 노가다를 통해서 해결하는 것도 하나의 방법이라고 생각합니다.

 

 이를테면 level이라는 클래스를 통제하고 싶을 때, .level {라이트모드일때 css}, .darkmode .level {다크모드일 때 css} 이런식으로 잔뜩 CSS를 작성해준 후, @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) 안에 전부 집어넣어서 ie에서만 불러오는 방식으로 대응하면 된다고 생각합니다.

 

관련글

자바스크립트로 반응형 벽돌 레이아웃 (Masonry Layout) 구현 해보기

자바스크립트로 캐러셀 슬라이드를 구현 해보기

간편 메모장
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.

복사하기  저장
× 내용 모두 지우기 내용 모두 복사하기
메모장입니다. 시크릿 모드가 아니면 내용이 저장됩니다.