다크모드 간단하게 토글로 구현해보기

근데 말이 간단이지 헐 나 이것도 못해 개발자 하면 안되나봐 ㅠㅠ 이럴 정도는 아닙니다… 

Reference

https://blogpack.tistory.com/1117 (다크모드 토글 기능 구현과 다크모드 토글 디자인 구현)

https://www.w3schools.com/howto/howto_css_switch.asp (How TO – Toggle Switch)

https://codechacha.com/ko/javascript-foreach/ (JavaScript – forEach(), 다양한 예제로 이해하기)


들어가기 전에: 딥-다크-모드

VScode
Wikipedia

요즘은 많이 쓰이고 있는 다크모드는 배경이 어두운 색인 모드로, 다크 테마라고도 한다. OS나 브라우저, 각종 앱(게임 말고)에서도 지원하는 기능으로, 개발자들 IDE는 다크테마가 국룰. 밝으면 벌레가 꼬인다나… 본인은 PC에서는 디스코드만 다크모드고 폰/패드는 일출/일몰 시간에 따라 다르다. 그래서 내 폰은 해 떨어지면 다크모드가 된다.

그런데 왜 이놈을 쓰는거죠? 일단 나도 가끔 개발하시는 분들을 보는데 다 IDE가 어두운 색이라 한번 물어본 적 있었다.

https://okky.kr/articles/1088381

다크모드가 익숙해서, 기본 설정이라서 쓰시는 분들도 계셨지만 눈이 덜 피곤해서 쓰신다고… 그리고 낮에는 햇빛이 있으니까 상관 없지만 밤에 캄캄한데 계속 밝은 화면을 보면

이렇게 된다. 그리고 밝은 화면을 계속 보여주게 되면 배터리도 훅훅 떨어진다. 자고로 빛의 혼합은 가산 혼합이라서 흰색 배경을 띄울 때 RGB를 풀로 쏴제껴야 흰색이 되거든… 

물론 CSS에서 색깔 잘못 잡으면 라이트모드에서는 잘 보이던 UI가 다크모드로 바꿨더니 안 보이는 경우도 있다.


초간단 다크모드 토글하기 

그래서 이번에는 뭘 만들거냐…

이걸 만들거다. 이미지를 잘 보면 글이랑 슬라이드 버튼이 있고, 글상자 두개 밑에 보이는 버튼도 다크모드를 토글할 때 색깔이 바뀐다.

<html>

<head>
    <title>딥-다크-모드</title>
    <meta charset="utf-8">
    <link href="style.css" rel="stylesheet">
    <script src="https://kit.fontawesome.com/dc58858c96.js" crossorigin="anonymous"></script>
</head>

<body>
    <div class="wrap">

        <div class="title">
            <h1>Deep-Dark-mode</h1>
            <p>아래 슬라이드 버튼을 눌러 라이트모드와 다크모드를 테스트해보세요! </p>
            <label class="switch" id="toggle">
                <input type="checkbox">
                <span class="slider round"></span>
            </label>
        </div>

        <div class="article">
            <h1>Title: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
                dolore magna aliqua.</h1>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
                dolore magna aliqua. Lacinia at quis risus sed vulputate. Et tortor consequat id porta nibh venenatis.
                Tellus integer feugiat scelerisque varius morbi enim nunc faucibus a. At lectus urna duis convallis.
                Lorem ipsum dolor sit amet consectetur adipiscing elit. Nec feugiat nisl pretium fusce id velit ut
                tortor. Nulla facilisi cras fermentum odio eu feugiat pretium. Consequat semper viverra nam libero justo
                laoreet sit amet cursus. Orci sagittis eu volutpat odio facilisis mauris sit amet. Pellentesque sit amet
                porttitor eget dolor morbi non arcu. Et netus et malesuada fames ac turpis egestas. Pellentesque
                habitant morbi tristique senectus et netus et malesuada fames. Augue interdum velit euismod in. Urna
                cursus eget nunc scelerisque viverra mauris in aliquam sem. Ac tortor dignissim convallis aenean et
                tortor at risus. Pulvinar elementum integer enim neque volutpat ac tincidunt.</p>
            <h1>Title: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
                dolore magna aliqua.</h1>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
                dolore magna aliqua. Commodo nulla facilisi nullam vehicula ipsum. Nullam ac tortor vitae purus faucibus
                ornare suspendisse. Tortor at auctor urna nunc id cursus metus. Diam vulputate ut pharetra sit amet
                aliquam id. Tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius. In est ante in nibh
                mauris cursus mattis molestie a. Scelerisque purus semper eget duis at tellus. Pharetra massa massa
                ultricies mi quis hendrerit dolor. Neque ornare aenean euismod elementum nisi.</p>
        </div>
        <button class="button" type="button">Button 1</button>
        <button class="button" type="button">Button 2</button>
        <button class="button" type="button">Button 3</button>
    </div>
    <script src="script.js"></script>
</body>

</html>

HTML(슬라이드 버튼은 W3schools에 있는 소스 갖다 배경색만 바꿨음)

@font-face {
    font-family: 'KyoboHand';
    src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_20-04@1.0/KyoboHand.woff') format('woff');
    font-weight: normal;
    font-style: normal;
}

:root {
    --dark-color: #32475e;
    --green-color: #41b883;
    --white-color: #f9f9f9;
    --yellow-color: #ffd41d;
}

* {
    font-family: 'KyoboHand';
    font-size: 16pt;
    margin: 0;
    padding: 0;
    line-height: 1.5em;
}

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

.dark-mode {
    background-color: var(--dark-color);
    color: var(--white-color);
}

h1,
p {
    margin-bottom: 5px;
}

h1 {
    font-size: 1.5em;
}

.wrap {
    width: 1280px;
    margin: 0 auto;
}

.title,
.article {
    border: 5px solid var(--green-color);
    border-radius: 7.5px;
    margin: 20px auto;
    padding: 10px;
}

/* The switch - the box around the slider */
.switch {
    position: relative;
    display: inline-block;
    width: 60px;
    height: 34px;
    margin-top: 10px;
}

/* Hide default HTML checkbox */
.switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

/* The slider */
.slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: var(--dark-color);
    -webkit-transition: .4s;
    transition: .4s;
}

.slider:before {
    position: absolute;
    content: "";
    height: 26px;
    width: 26px;
    left: 4px;
    bottom: 4px;
    background-color: var(--white-color);
    -webkit-transition: .4s;
    transition: .4s;
}

input:checked+.slider {
    background-color: var(--green-color);
}

input:focus+.slider {
    box-shadow: 0 0 1px var(--green-color);
}

input:checked+.slider:before {
    -webkit-transform: translateX(26px);
    -ms-transform: translateX(26px);
    transform: translateX(26px);
}

/* Rounded sliders */
.slider.round {
    border-radius: 34px;
}

.slider.round:before {
    border-radius: 50%;
}

button {
    border: 0;
    width: 150px;
    height: 50px;
    border-radius: 7.5px;
    background-color: var(--yellow-color);
    margin-right: 10px;
}

button:hover {
    background-color: var(--yellow-color);
}

CSS

let button = document.getElementById('toggle');

button.addEventListener("click", function() {
    if(document.querySelector('body').classList.contains('dark-mode')){
        document.body.classList.remove("dark-mode");
    }else{
        document.body.classList.add("dark-mode");
    }
},false);

JS

슬라이드 버튼은 버튼이 쇽쇽 밀려나가는 것 말고 사실상 체크박스나 라디오버튼이랑 다를 게 없다. 자바스크립트 코드는 버튼을 눌렀을 때 클래스에 dark-mode가 있으면 지우고(라이트모드로) 아니면 붙여라(다크모드). 근데 위 코드대로 하면 어떻게 되느냐… 글이랑 배경은 바뀌는데 버튼 색이 그대로예요. 그러면 버튼에도 이걸 줘야 하는데…

button.dark-mode {
    color: var(--white-color);
    background-color: var(--green-color);
}

버튼에도 dark-mode를 추가하고… 위 코드대로 주면 에러난다. 버튼이 세 개니까 다 바꾸려면 querySelector가 아니라 querySelectorAll로 가져와야 하는데 문제가 뭐냐면 저걸로 갖고오면 배열이 된다. 그래서 자바스크립트 입장에서는 아니 배열인데 클래스가 어딨음? 착한 사람만 보임? 이렇게 되기 때문에 반복문으로 배열을 순회해서 볼거다.

let togglebutton = document.getElementById('toggle');
let button = document.querySelectorAll('button')

togglebutton.addEventListener("click", function () {
    console.log('버튼!')
    if (document.querySelector('body').classList.contains('dark-mode')) {
        document.body.classList.remove("dark-mode");
    } else {
        document.body.classList.add("dark-mode");
    }
    button.forEach(function(item){
        if (item.classList.contains('dark-mode')) {
            item.classList.remove('dark-mode');
        }
        else {
            item.classList.add('dark-mode');
        }
    })
}, false);

버튼을 querySelectorAll로 가져오게 되면 배열이라고 했는데 그러면 못 주느냐? 놉. 반복문 주면 가능하다. 배열 안의 요소들에 dark-mode가 있으면 지우고(라이트모르도) 반대로 없으면 붙이게(다크모드로) 하면 된다. 하나가 아니라 뭉텅이라 배열로 들고 온 것 뿐이지 내용물이 변하는 건 아니거든.