Dark Mode / Light Mode 구현

이인재·2022년 9월 6일
0

Javascript

목록 보기
19/28

※ 자세한 코드는 github 참고
https://github.com/llinjae/Light-Dark-Mode.git

구현해야할 것

HTML

		<!-- Switch -->
        <div class="theme-switch-wrapper">
            <span id="toggle-icon">
                <span class="toggle-text">Light Mode</span>
                <i class="fas fa-sun"></i>
            </span>
            <label class="theme-switch">
                <input type="checkbox">
                <div class="slider round"></div>
            </label>
        </div>
        <!-- nav -->
        <nav id="nav">
            <a href="#home">HOME</a>
            <a href="#about">ABOUT</a>
            <a href="#projects">PROJECTS</a>
            <a href="#contact">CONTACT</a>
        </nav>
        <!-- Home -->
        <section id="home">
            <div class="title-group">
                <h1>Custom Title Here</h1>
                <h2>Welcome to the Website!</h2>
            </div>
        </section>
        <!-- About -->
        <section id="about">
            <h1>Undraw Illustrations</h1>
            <div class="about-container">
                <div class="image-container">
                    <h2>Web Innovation</h2>
                    <img src="img/undraw_proud_coder_light.svg" alt="Proud Coder" id="image1">
                </div>
                <div class="image-container">
                    <h2>Problem Solving</h2>
                    <img src="img/undraw_feeling_proud_light.svg" alt="Proud" id="image2">
                </div>
                <div class="image-container"> 
                    <h2>High Concept</h2>   
                    <img src="img/undraw_conceptual_idea_light.svg" alt="Idea" id="image3">
                </div>
            </div>
        </section>
        <!-- Projects -->
        <section id="projects">
            <h1>Buttons</h1>
            <div class="buttons">
                <button class="primary">Primary</button>
                <button class="secondary">Secondary</button>
                <button class="primary" disabled>Disabled</button>
                <button class="outline">Outline</button>
                <button class="secondary outline">Alt Outline</button>
                <button class="outline" disabled>Disabled</button>
            </div>
            <div class="text-box" id="text-box">
                <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores nihil vel numquam. Velit sint distinctio, non voluptate laborum iusto possimus eveniet sed voluptatem delectus incidunt ut debitis, itaque, odio atque?</p>
            </div>
        </section>
        <!-- Contact -->
        <section id="contact">
            <div class="social-icons">
                <i class="fab fa-github"></i>
                <i class="fab fa-codepen"></i>
                <i class="fab fa-linkedin-in"></i>
                <i class="fab fa-medium"></i>
                <i class="fab fa-instagram"></i>
                <i class="fab fa-youtube"></i>
            </div>
        </section>

CSS

※ dark mode 전환에 관련된 코드만 작성

Light Mode에서 쓰이는 색상 설정

:root {
  --primary-color: rgb(255, 92, 92);
  --primary-variant: #ff2d2d;
  --secondary-color: #1b9999;
  --on-primary: rgb(250, 250, 250);
  --on-background: rgb(66, 66, 66);
  --on-background-alt: rgba(66, 66, 66, 0.7);
  --background: rgb(255, 255, 255);
  --box-shadow: 0 5px 20px 1px rgba(0, 0, 0, 0.5);
}

Dark Mode에서 쓰이는 색상 설정

[data-theme="dark"] {
  --primary-color: rgb(150, 65, 255);
  --primary-variant: #6c63ff;
  --secondary-color: #03dac5;
  --on-primary: #000;
  --on-background: rgba(255, 255, 255, 0.9);
  --on-background-alt: rgba(255, 255, 255, 0.7);
  --background: #121212;
}

즉, 같은 변수를 사용함으로써 light일 때는 :root 안에 있는 것들을,
Dark일 때는 [data-theme="dark"] 안에 있는 것들로 사용

하지만, 이미지같은 경우에는 css 내에서만으로 색깔 변경이 어렵기에 light일 때의 이미지와 dark일 때의 이미지 두가지 버전으로 준비해둔다.

일단 light 버전의 이미지를 기본값으로 설정해준다.

나머지는 JS를 통해 바꿔준다.


JavaScript

코드 1

function darkMode() {
    nav.style.backgroundColor = 'rgb(0 0 0 / 50%)';
    textBox.style.backgroundColor = 'rgb(255 255 255 / 50%)';
    toggleIcon.children[0].textContent = 'Dark Mode';
    toggleIcon.children[1].classList.remove('fa-sun');
    toggleIcon.children[1].classList.add('fa-moon');
    image1.src = 'img/undraw_proud_coder_dark.svg';
    image2.src = 'img/undraw_feeling_proud_dark.svg';
    image3.src = 'img/undraw_conceptual_idea_dark.svg';
}


function lightMode() {
    nav.style.backgroundColor = 'rgb(255 255 255 / 50%)';
    textBox.style.backgroundColor = 'rgb(0 0 0 / 50%)';
    toggleIcon.children[0].textContent = 'Light Mode';
    toggleIcon.children[1].classList.remove('fa-moon');
    toggleIcon.children[1].classList.add('fa-sun');
    image1.src = 'img/undraw_proud_coder_light.svg';
    image2.src = 'img/undraw_feeling_proud_light.svg';
    image3.src = 'img/undraw_conceptual_idea_light.svg';
}

이렇게 darkMode(), lightMode() 함수를 생성해준다.

nav, textBox의 배경색을 서로 반대가 되게 설정한다..

toggleIcon의 첫번째 자식의 텍스트 내용도 Mode에 따라 변경되도록 설정한다.

toggleIcon의 두번째 자식은
darkMode에서는 fa-sun 아이콘을 제거하고 fa-moon 아이콘을 추가
lightMode에서는 fa-moon 아이콘을 제거하고 fa-sun 아이콘을 추가

그리고 마지막으로, 이미지의 src를 바꾸어준다.
여기서 핵심 사항은 이미지의 이름을 light, dark로 구분짓는게 중요하다.


위의 코드를 활용해 동적으로 작동시키려면 더 많은 코드가 작성되어야 한다.

function switchTheme(event) {
    if (event.target.checked) {
        document.documentElement.setAttribute('data-theme', 'dark');
        darkMode();
    } else {
        document.documentElement.setAttribute('data-theme', 'light');
        lightMode();
    }
}

toggleSwitch.addEventListener('change', switchTheme);

event.target.checked에서 event.target은 input을 가리키고
그 속성 중에 checked라는 속성이 있다.

만약 event.target.checked = true이면 darkMode인 것이고
false이면 lightMode인 것이다.

그러니 코드에서는
event.target.checked = true이면
document.documentElementhtmldata-theme가 dark가 되도록

event.target.checked = false이면
document.documentElementhtmldata-theme가 light이 되도록 한다.

toggleSwitch에 change 이벤트를 적용하면 switchTheme 함수를 실행시킨다.


코드 2

그런데 코드 1은 너무 반복되게 작성한 것이 많다...

그래서 새로운 함수를 만드는 등 코드를 조금 수정해보았다.

function imageMode(color) {
    image1.src = `img/undraw_proud_coder_${color}.svg`;
    image2.src = `img/undraw_feeling_proud_${color}.svg`;
    image3.src = `img/undraw_conceptual_idea_${color}.svg`;
} 
function toggleDarkLightMode(isDark) {
    nav.style.backgroundColor = isDark ? 'rgb(0 0 0 / 50%)' : 'rgb(255 255 255 / 50%)';
    textBox.style.backgroundColor = isDark ? 'rgb(255 255 255 / 50%)' : 'rgb(0 0 0 / 50%)';
    toggleIcon.children[0].textContent = isDark ? 'Dark Mode' : 'Light Mode';
    isDark ? toggleIcon.children[1].classList.replace('fa-sun', 'fa-moon') : toggleIcon.children[1].classList.replace('fa-moon', 'fa-sun');
    isDark ? imageMode('dark') : imageMode('light');
}
function switchTheme(event) {
    if (event.target.checked) {
        document.documentElement.setAttribute('data-theme', 'dark');
        toggleDarkLightMode(true);
    } else {
        document.documentElement.setAttribute('data-theme', 'light');
        toggleDarkLightMode(false);
    }
}

imageMode라는 함수를 생성해 image에 대한 src를 하나씩 선언하는 게 아니라 템플릿 리터럴을 통해
lightMode일 때는 light 이미지를 darkMode일 때는 dark 이미지를 적용시킨다.

또한, darkMode(), lightMode() 함수를 둘 다 작성해서 나누는 게 아니라 삼항연산자를 통해 설정해주면 더 간결하고 깔끔해진다.

toggleDarkLightMode()함수를 만들어주었으니 switchTeme()에서 실행한 darkMode(), lightMode()를 삭제하고 toggleDarkLightMode()에 true나 false를 집어넣어 이전 코드와 결과가 같게 만들어준다.


localStorage

데이터를 저장해주기 위해 localStorage에 저장해준다.

function switchTheme(event) {
    if (event.target.checked) {
        document.documentElement.setAttribute('data-theme', 'dark');
        localStorage.setItem('theme', 'dark');
        toggleDarkLightMode(true);
    } else {
        document.documentElement.setAttribute('data-theme', 'light');
        localStorage.setItem('theme', 'light');
        toggleDarkLightMode(false);
    }
}

단순하게 localStorage.setItem으로 key, value값을 저장한다.

const currentTheme = localStorage.getItem('theme');
if (currentTheme) {
    document.documentElement.setAttribute('data-theme', currentTheme);

    if (currentTheme === 'dark') {
        toggleSwitch.checked = true;
        toggleDarkLightMode(true);
    }
}

처음 웹사이트에 접근하면 localStrage는 null일 것이다.

만약에 우리가 switch를 통해 darkMode로 변경했다고 하면 localStorage에 theme, dark가 저장된 것을 볼 수 있다.

그런데...?? 새로고침했더니 화면은 lightMode인데 여전히 localStorage에 표시된 것은 dark이다...

이를 해결하기 위해서는 if문을 사용해서 해당 테마에 대한 값이 있을 때 그 함수를 실행되도록 한다.

만약 처음 if문만 작성했다면 화면이 다음과 같이 나온다.

dark모드가 기본값이 되었는데 새로고침을 누르면 분명 화면에는 lightMode switch로 바뀌어 있는데 대부분이 darkMode 형태인 것을 알 수 있다.

이를 해결하기 위해서는 또다른 if문이 필요하다.
위 코드와 같이 if문을 추가해준다면 정상적으로 실행하는 것을 볼 수 있다.

코드 3

코드 2에서 더 고칠 것이 있다면, 더 반복을 줄이는 것이다.
dark나 light의 반복되는 것이 있기에

const DARK_THEME = 'dark';
const LIGHT_THEME = 'light';

이런식으로 변수를 설정해주고 바꿔주는 것도 하나의 방법이다.

0개의 댓글