웹에서 다크 모드 구현하기

개발

웹에서 다크 모드 구현하기
최종 수정일:

다크 모드(다크 테마)란, 밝은 배경에 어두운 글자 대신 어두운 배경에 밝은 글자를 사용하는 것입니다. 눈의 피로를 줄이기 위해 선호하는 사람이 꽤 있고, 콘트라스트(Contrast)가 높아지기에 텍스트나 이미지를 강조하는 데도 효과적입니다만, 긴 글을 표시해야 할 땐 독이 될 수 있으며, 난시 등의 이유로 어두운 배경에선 글자를 읽기 어려워하는 사용자도 있어 호불호도 꽤 갈리는 편입니다.
그럼에도 Windows 10, MacOS, Android, iOS 등의 운영체제뿐 아니라, Youtube, Instagram, Discord, Netflix 등의 웹 및 앱에서도 널리 사용됩니다. 호불호가 갈리는 만큼 대부분 사용 여부를 사용자가 결정하게 합니다.

선호 테마 감지하기

상술했듯 다크 테마는 꽤 호불호가 갈리기에, 사용자가 어떤 테마를 선호하는지 먼저 확인할 필요가 있습니다.

CSS

@media (prefers-color-scheme: dark) {
    /* Your CSS */
}

prefers-color-scheme을 이용하시면 사용자가 어떤 테마를 선호하는지 알아낼 수 있습니다.
light는 라이트 테마, dark는 다크 테마, no-preference는 선호하는 테마 없음 입니다.

Javascript

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

테마 변경 기능을 넣지 않으려면 위 CSS로 충분하겠지만, 테마를 변경하는 기능을 추가하시려면 JS에서 선호 테마를 감지할 필요가 있습니다.
window.matchMedia를 이용해 JS에서도 미디어 쿼리를 사용하실 수 있습니다.

다크 테마 추가

Pure CSS

테마를 변경하지 않고 시스템 테마만 따르려면 CSS로만 작업하셔도 충분합니다.

a {
    color: #0000ee;
}
 
@media (prefers-color-scheme: dark) {
    body {
        background: #121212;
        color: #bbb;
    }
 
    a {
        color: #3ea6ff;
    }
}

CSS는 스타일이 겹치면 더 아래에 있는 걸 적용하기 때문에, 반드시 다크 테마의 스타일이 아래로 오게만 해주시면 됩니다.

Class

if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
    document.documentElement.classList.add("dark");
}

사용자가 다크 테마를 사용하면 html(body도 무관)에 dark란 class를 추가하게 했습니다.

이 스크립트를 body 태그가 끝나는 지점 앞에 추가하면 렌더링을 차단하진 않지만, 사용자가 다크 테마를 사용해도 스크립트를 불러오기 전까진 밝은 배경이 보일 겁니다.
head 태그 내부에 추가하면 밝은 배경은 보이지 않지만, 렌더링을 차단합니다.

html {
    color: #343434;
}
 
html.dark {
    background: #121212;
    color: #bbb;
}
 
html.dark a {
    color: #3ea6ff;
}

CSS는 Pure CSS로 작성한 것과 크게 다르지 않지만

document.getElementById("toggleTheme").addEventListener("click", () => {
    document.documentElement.classList.toggle("dark");
});

button#toggleTheme을 추가하시고 위 스크립트를 추가하시면 이제 라이트와 다크 테마를 토글할 수 있게 되었습니다.

CSS 변수

위의 html class를 토글한 것과 똑같이 html의 class를 토글하지만,

html {
    --bg-color: #fff;
    --text-color: #343434;
    --link-color: #0000ee;
}
 
html.dark {
    --bg-color: #121212;
    --text-color: #bbb;
    --link-color: #3ea6ff;
}
 
body {
    background: var(--bg-color);
    color: var(--text-color);
}
 
a {
    color: var(--link-color);
}

CSS 변수를 이용해 html에 색상 값을 선언해두고, bodya 태그의 스타일은 한 번씩만 작성했습니다.

지금은 워낙 HTML과 CSS가 간단해 위 CSS보다 길어졌지만, 이들이 복잡해지면 복잡해질수록 이 방법이 편하실 겁니다.

하나 안타까운 건, 굉장히 오래된 브라우저는 CSS 변수를 지원하지 않는단 것입니다. (참고 : Can I Use)

선호 테마 저장하기

페이지를 불러올 때마다 선호하는 테마를 사용하기 위해 버튼을 클릭할 순 없으니, 이를 저장할 차례입니다.
쿠키, localStorage ,IndexedDB 등에 저장하실 수 있습니다만, 쿠키는 이런 정보를 저장하려고 있는 게 아니고, IndexedDB에 넣을 만큼 크지도 않기에, localStorage에 저장하겠습니다.

* 본문과 크게 관련 없지만, localStorage엔 문자열만 저장할 수 있고, 메인 쓰레드를 차단할 여지도 있기에, 큰 데이터를 다루실 땐 IndexedDB를 이용하시는 걸 추천합니다.

document.getElementById("toggleTheme").addEventListener("click", () => {
    const html = document.documentElement;
 
    if (html.classList.contains("dark")) {
        html.classList.remove("dark");
        localStorage.setItem("darkTheme", "false");
    } else {
        html.classList.add("dark");
        localStorage.setItem("darkTheme", "true");
    }
});

버튼을 클릭할 때, html의 class만 토글하지 않고, localStorage에 darkTheme의 활성화 여부를 저장하게 했습니다.

const storedTheme = localStorage.getItem("darkTheme");
 
if (storedTheme !== null) {
    if (storedTheme === "true") {
        document.documentElement.classList.add("dark");
    }
} else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
    document.documentElement.classList.add("dark");
}

다음에 테마를 확인할 때 localStorage에 저장된 게 있는지 확인을 우선 해주고, 저장된 게 없다면 prefers-color-scheme을 확인하시면 됩니다.

상술한 것과 마찬가지로, 스크립트를 head 태그에 넣으시면 렌더링을 차단하지만 다크 테마를 선호하는 사용자가 흰 화면을 볼 일이 없고, body 태그가 끝나는 지점에 넣으시면 렌더링을 차단하지 않지만 다크 테마를 선호하는 사용자도 스크립트를 불러오고 실행하기 전까지 흰 화면을 봐야 합니다.

User Agent 스타일을 위한 메타 태그

사실상 위에서 끝이긴 합니다만, 혹 테마 토글 기능 없이 시스템의 테마를 따르면서 웹사이트를 제작하시려면 추가하시는 게 좋은 메타 태그입니다.
아무것도 하지 않아도 브라우저에서 추가하는 CSS(User Agent Stylesheet)가 시스템의 테마에 맞게 조정됩니다.

<meta name="color-scheme" content="dark light" />
color-scheme 메타 태그

메타 태그 없음(좌) / 메타 태그 있음(우)

CSS는 한 줄도 작성하지 않고 input[type="text"]input[type="radio"] 두 개만 추가한 페이지를 iOS 사파리에서 봤을 때 차이입니다.
보시는 것처럼 배경색부터, input, button 등의 요소의 색상을 조정해줍니다.

디자인

배경색

단순히 다크 테마라고 #000을 넣으시면 보기 상당히 이상한 디자인이 탄생해버립니다.
Material Theme에선 #121212를 권고하고, 대부분 #121212 ~ #363636 정도 사이를 사용합니다.

글자색

마찬가지로 #fff은 너무 밝을 수 있으니, 정말 강조하고 싶은 문구가 아니라면 피하시는 게 좋습니다.
강조하고 싶은 텍스트엔 #f1f1f1를,
일반적인 텍스트엔 #bbb를 사용하시는 걸 권장합니다만, 꽤 밝은 글자색을 사용하는 사이트도 많으며, windows 10은 심지어 배경색에 #000, 글자색에 #fff를 사용하기도 합니다.

배경색 #121212를 기준으로, #9f9f9f까지 AAA 레벨을 통과하니 참고해주세요.

올바른 색 고르기

다크 모드 색상

보통 다크 테마는 눈을 편하게 하고자 사용하는데, 어두운 배경에 강렬한 색만큼 눈이 피곤한 게 없습니다.
소이 물 빠진 색이라 불리는 강렬하지 않은 색상을 사용하시는 걸 추천합니다.

Report an issue