├── .gitignore ├── LICENSE ├── README.en.md ├── README.jp.md ├── README.md ├── assets ├── css │ ├── content.css │ ├── dark.css │ └── reset.css └── js │ ├── dark.js │ └── example.js ├── img ├── ani.gif └── dark-diff.png └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Seung-hyun Hwang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # 🌙 Dark Mode Web Page design 2 | 3 | - [한국어](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.md) 4 | - [English](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.en.md) 5 | - [日本語](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.jp.md) 6 | 7 | ## Table of contents 8 | 1. [What is the Dark mode?](#what-is-the-dark-mode) 9 | 2. [Why should we use Dark Mode?](#why-should-we-use-dark-mode) 10 | 3. [Recognizing Dark mode](#recognizing-dark-mode) 11 | 4. [Turn Dark mode on and off](#turn-dark-mode-on-and-off) 12 | 5. [Animation](#animation) 13 | 14 | ## What is the Dark Mode? 15 | On September 20, 2019, at 6:00 p.m., Apple began to release iOS 13, a new operating system for iPhone and iPod Touch. In addition, at WWDC2019 (Apple WorldWide Developers Conference) held in early June of the same year, developers in Apple provided a way that web developers can control Dark Mode for iOS13 and MacOS Mojave. Unlike the previous UI, which shows black letters on the white background, Dark Mode shows white letters on the black background. 16 | 17 | ## Why should we use Dark Mode? 18 | Why should we use Dark Mode? That's because it's in fashion these days and looks cool? There're more fundamental reasons in this question. 19 | 20 | The most important purpose of webpages is to clearly convey information of the webpage to the users. Movie theater staffs turn off the lights in the theater so that they make the inside space dark before the movie starts. That's because the focus on the central contents is likely to be distributed outside when the surrounding elements are too fancy or conspicuous. Therefore, dark environments including Dark Mode allow you to focus more on the central contents than on the surrounding elements because dark things aren't usually fancy or conspicuous. The simple UI design patterns that we use these days are in the same vein. 21 | 22 | Developers have a duty to make software easy for all users to use regardless of whether or not they have disability. Apple's 'VoiceOver' is an accessibility tool, Apple's flagship for blind people, and that's why many blind people choose Apple's iPhone. In addition, the function of "inverting colors" on entire screen is helpful for many users that have low vision or is sensitive to bright lights. 23 | 24 | Likewise, Dark Mode also can have a good effect on users, who have low vision or is sensitive to bright lights, by reducing their eyestrain. You may be anxious that it might be turned into bright colors again when you visit a webpage using Dark Mode with the function of inverting colors turned on, but you don't have to be worried! The function of Apple's "Smart Invert" is working to invert colors of screen in accordance with checking whether or not the webpage is using Dark Mode, and the function doesn't invert the color of the screen when the webpage is using Dark Mode. 25 | 26 | Imagine that a user unlock dark lock screen in his or her smartphone, open dark web browser, and access your web site by entering an address in dark address bar. Oops!! The user whose device is using Dark Mode is very suprised by dazzling lights when the user access your website that isn't supporting Dark Mode. ~Doctor, I can't see at all!!~ Developers have to offer users unified UX. The different UI from the environment of users might make them feel like leaving your website as soon as possible. 27 | 28 | For this reason, I'll tell you how to automatically change the website to dark design when users using Dark Mode access it. 29 | 30 | ## Recognizing Dark mode 31 | You have to check out whether or not the device of users is allowed to use Dark Mode so as to apply Dark Mode the moment users access a website. Here's how you can check for it on CSS and JavaScript: 32 | 33 | #### CSS 34 | CSS supports a `prefer-color-cheme` media query that tells you what kind of theme the device of users has. `prefers-color-cheme` can have the following values: 35 | 36 | - no-preference: Not telling what kind of theme the device has. 37 | - light: Using light mode. 38 | - dark: Using Dark Mode. 39 | 40 | Therefore, you can write CSS codes, which work only for users using Dark Mode, with the code below: 41 | ```css 42 | @media (prefers-color-scheme: dark) { 43 | body { 44 | background: black; 45 | color: white; 46 | } 47 | } 48 | ``` 49 | 50 | However, some browsers may don’t support `prefers-color-cheme` media queries and have different values from the settings of system-theme on the device of the users. 51 | 52 | - ✅ : Supporting prefers-color-cheme media queries. 53 | - ❌ : Not supporting them or having different values from the settings of system-theme on the device of users. 54 | 55 | | OS / Browser | Safari | Chrome | Firefox | Whale | 56 | | ------------- |:------:|:------:|:-------:|:-----:| 57 | | iOS |✅ |❌ |❌ |❌ | 58 | | iPadOS |✅ |❌ |❌ |❌ | 59 | | macOS |✅ |✅ |✅ |✅ | 60 | 61 | 62 | #### JavaScript 63 | In JavaScript, after referencing the media query of CSS, you check out whether or not the device of users is supporting the `prefers-color-cheme` media query, determining compatibility of the device. 64 | 65 | ```javascript 66 | const darkModeMeidaQuery = window.matchMedia('(prefers-color-scheme: dark)'); 67 | 68 | function updateForDarkModeChange() { 69 | if (darkModeMeidaQuery.matches) { 70 | // Dark mode is on. 71 | } else { 72 | // Dark mode is off. 73 | } 74 | } 75 | 76 | darkModeMeidaQuery.addListener(updateForDarkModeChange); 77 | updateForDarkModeChange(); 78 | ``` 79 | 80 | You can succeed to support Dark Mode by applying the following code: 81 | 82 | ```css 83 | @media (prefers-color-scheme: dark) { 84 | body { 85 | background: #323232; 86 | color: #fff; 87 | } 88 | 89 | header { 90 | background: #111; 91 | } 92 | 93 | footer { 94 | background: #111; 95 | } 96 | } 97 | ``` 98 | 99 | ![Dark mode](img/dark-diff.png) 100 | All CSS codes associated with Dark Mode are written in one media query. Therefore, you can activate the whole media code of a file by using the attribute of `media` in `link` tag after writing CSS codes in the file of `dark.css`. 101 | 102 | #### dark.css 103 | ```css 104 | /* 105 | media=(prefers-color-scheme: dark) 106 | */ 107 | 108 | body { 109 | background: #323232; 110 | color: #fff; 111 | } 112 | 113 | header { 114 | background: #111; 115 | } 116 | 117 | footer { 118 | background: #111; 119 | } 120 | ``` 121 | 122 | #### index.html 123 | ```html 124 | ... 125 | 128 | ... 129 | ``` 130 | 131 | ## Turn Dark Mode on and off 132 | The recognition function of Dark Mode works only on some browsers and devices, including iOS13, iPadOS, and MacOS Mojave. Dark Mode doesn’t work in case you don't use the latest apple device and the browser which are supporting the function of prefers-color-scheme media query. From now on, I'll tell you how to make Dark Mode available for users who use a device that doesn't support Dark Mode function. 133 | 134 | 135 | You can enable or disable the CSS codes by manipulating the attribute of `media` in `link` tag that you added in order to detach the file. First, make buttons so that you can control `darkModeSwitch`, the function of JavaScript that turns Dark Mode on/off. 136 | 137 | 138 | 139 | ```html 140 |

141 | Dark Mode: 142 | ON 143 | / 144 | OFF 145 |

146 | ``` 147 | 148 | Then, assign an id to the `link` tag that you added in order to reference `dark.css` above. 149 | 150 | ```html 151 | 155 | ``` 156 | 157 | And define the `darkModeSwitch` function above. In this, you will manipulate the attribute of `media` in the `link` tag. 158 | ```javascript 159 | function darkModeSwitch(status) { 160 | document 161 | .querySelector('#dark-mode-sheet') 162 | .setAttribute( 163 | 'media', 164 | status? 'screen' : 'not screen' 165 | ); 166 | } 167 | ``` 168 | 169 | By doing this, you can add function of turning on/off Dark Mode. However, Dark Mode will be turned off when users reload the page in your browser or move from one page to another. This problem will be solved when you save the set value in your browser by using Cookie and let the webpage displayed according to the value. 170 | 171 | In this article, I use the `JavaScript Cookie` library for controlling cookies. 172 | [Learn more](https://github.com/js-cookie/js-cookie) 173 | 174 | First, save it as a cookie when Dark Mode is turned on. You have to save the set value after converting it into an integer type because Cookies store values as a string. And add `isDarkMode` function that obtains this value. 175 | ```javascript 176 | function isDarkMode() { 177 | return Cookies.get('darkmode'); 178 | } 179 | 180 | function darkModeSwitch(status) { 181 | Cookies.set('darkmode', +status); 182 | document 183 | .querySelector('#dark-mode-sheet') 184 | //omitted 185 | } 186 | ``` 187 | 188 | Programs have to manipulate the webpage according to this value when the webpage starts. If there is a cookie value, they manipulate the webpage according to the cookie value instead of using the set value of Dark Mode in the device of users. They convert the stored value into a integer because it is a integer string. 189 | ```javascript 190 | //the preface omitted 191 | document.addEventListener('DOMContentLoaded', function () { 192 | const isDm = isDarkMode(); 193 | if (isDm != null) darkModeSwitch(+isDm); 194 | }); 195 | ``` 196 | 197 | By doing this, Dark Mode can fully support to all users. 198 | 199 | ## Animation 200 | You can get much higher quality design of website after you add an animation that is operated whenever Dark Mode is turned on. 201 | 202 | ![Animation](img/ani.gif) 203 | ```css 204 | body { 205 | transition: .5s background, .5s color; 206 | } 207 | 208 | header { 209 | transition: .5s background; 210 | } 211 | 212 | footer { 213 | transition: .5s background; 214 | } 215 | ``` 216 | 217 | You can see the page used in test by clicking [here](https://tmdgus0084.github.io/apple-dark-mode/). 218 | I hope you will explore more resources and develop for a wide variety of experiences of many users. Please click this STAR⭐️ if this article is helpful!! 219 | Thank you for reading. 220 | 221 | 222 | 223 | ## Contributors 224 | - [Seung-hyun Hwang@tmdgus0084](https://github.com/tmdgus0084): Project Manager 225 | - [Hyunwoo Park@lqez](https://github.com/lqez): Fixing typo 226 | - [@Bgh0602](https://github.com/Bgh0602): English translation 227 | - [@sjbhm18](https://github.com/sjbhm18): Proofreading all sentences in Korean(Original Ver.) and translating them into English/Japanese 228 | -------------------------------------------------------------------------------- /README.jp.md: -------------------------------------------------------------------------------- 1 | # 🌙 ダークモードウェブページデザイン 2 | 3 | - [한국어](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.md) 4 | - [English](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.en.md) 5 | - [日本語](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.jp.md) 6 | 7 | ## 目次 8 | 1. [ダークモードとは](#ダークモードとは) 9 | 2. [ダークモードを使用する時のメリットは何?](#ダークモードを使用する時のメリットは何) 10 | 3. [ダークモードの認識](#ダークモードの認識) 11 | 4. [ダークモードのオン・オフ](#ダークモードのオンオフ) 12 | 5. [アニメーション](#アニメーション) 13 | 14 | ## ダークモードとは 15 | 2019年9月20日午前2時、アップルはiPhoneとiPodTouchに向けた新しい運営体制であるiOS 13を公開し始めました。また、同年6月初旬に開かれたWWDC2019(アップル世界開発者会議)では、ウェブ開発者がiOS13とMacOS Mojaveを向けたダークモードを制御できる方法を提供しました。白い画面の上に黒い文字を表す以前のUIとは違って、このダークモードは黒い画面に白い文字を表して全体的に暗い姿を見せるUIです。 16 | 17 | ## ダークモードを使用する時のメリットは何? 18 | ダークモードを使用すれば何のメリットがあるでしょうか。最近流行ってるから? 素晴らしくて? これよりもっと根本的な理由があります。 19 | 20 | ウェブページでの最重要の目的は、ウェブページの情報をユーザーに明確に伝える事です。映画館の職員は映画が始まる前に一番先に上映館の照明を消して周辺を暗くします。 その理由は、周りを飾る要素があまりにも派手派手しかったり、目立ったりすると中心のコンテンツへの集中度は外部へ分散されやすいからです。映画館の事と同じく、ダークモードを含めた色んな暗い環境は周辺要素より、中心のコンテンツにもっと集中できるようにしてくれます。 最近よく使われるシンプルなUIデザインパターンもこれと同じ脈絡です。 21 | 22 | 開発者には、使用者の障害とはおかまいなしに全ての使用者が便利にソフトウェアを使用できるように提供する義務が有ります。アップルの「VoiceOver」は視覚障害者のためのアップルの代表的なアクセスツールで、多くの視覚障害者がiPhoneを使用する理由になりました。この他にも、画面全体を反転させる「色反転」の機能が低視力者や明るい光に敏感なユーザーに助けになりました。 23 | 24 | ダークモードも低視力者や明るい光に敏感なユーザーに目の疲れを和らげる効果を与えるので、ユーザーに大いに役に立ちます。ユーザーが「色反転」の機能をOnにしてダークモードが使用されてるウェブページに訪問したら、再び明るい色になるのではないかと心配する方々がいるかもしれませんが、その心配はありません。アップルの「スマート反転」機能は、機に臨み変に応じて画面を適切に反転させる機能で、既にダークモードをサポートしているなら画面を反転させません。 25 | 26 | あるユーザーがスマホの暗いロック画面を解除して、暗いウェブブラウザを開いて、暗いアドレスバーにアドレスを入力して、ウェブサイトにアクセスしています。 あっ、ダークモードをサポートする機器を使っていたこのユーザーは、ダークモードをサポートしないウェブサイトにアクセスした瞬間、閃光弾を浴びたような感じを受けます。~~「お医者さん、前が全然見えません!」~~ 開発者はユーザーに統一性のあるUXを提供する必要があります。ユーザーが使用していた環境と懸け離れた環境のUIは、使用者に 抵抗感を経験させたり、ウェブサイトから一刻も早く離れたいという気持ちを与えたりする可能性が高いはずです。 27 | 28 | このような理由で、ダークモードを使用してるユーザーがウェブサイトにアクセスする場合、自動的にウェブサイトを暗いデザインに変更させる方法を教えてあげます。 29 | 30 | ## ダークモードの認識 31 | ユーザーがウェブサイトにアクセスした瞬間にダークモードを適用するためには、まずユーザーの機器がダークモードを使用しているかを確認しなければなりません。 CSSとJavaScriptでこれを確認する方法は次の通りです。 32 | 33 | #### CSS 34 | CSSは、ユーザ機器がどのようなテーマを使用するのかを教えてくれる`prefers-color-scheme`メディアクエリーをサポートします。`prefers-color-scheme`は次のような値を持つことができます。 35 | 36 | - no-preference: テーマ不詳 37 | - light: ライトモードを使用中 38 | - dark: ダークモードを使用中 39 | 40 | これで、以下のコードを利用して、ダークモードを使用するユーザーだけに作動するCSSコードを作成することができます。 41 | ```css 42 | @media (prefers-color-scheme: dark) { 43 | body { 44 | background: black; 45 | color: white; 46 | } 47 | } 48 | ``` 49 | 50 | しかし、一部のブラウザは、`prefers-color-scheme`メディアクエリーをサポートしなかったり、実際のユーザ機器のシステムテーマ設定とは異なる値を持ったりすることがあります。 51 | 52 | - ✅ : サポート中 53 | - ❌ : サポートしなかったり、実際のユーザ機器のシステムテーマ設定とは異なる値を持ったりする 54 | 55 | | OS / Browser | Safari | Chrome | Firefox | Whale | 56 | | ------------- |:------:|:------:|:-------:|:-----:| 57 | | iOS |✅ |❌ |❌ |❌ | 58 | | iPadOS |✅ |❌ |❌ |❌ | 59 | | macOS |✅ |✅ |✅ |✅ | 60 | 61 | 62 | #### JavaScript 63 | JSではCSSのメディアクエリーを借りて来た後、`prefers-color-scheme`メディアクエリーの支援有無を確認します。これで互換性が決まります。 64 | 65 | ```javascript 66 | const darkModeMeidaQuery = window.matchMedia('(prefers-color-scheme: dark)'); 67 | 68 | function updateForDarkModeChange() { 69 | if (darkModeMeidaQuery.matches) { 70 | // Dark mode is on. 71 | } else { 72 | // Dark mode is off. 73 | } 74 | } 75 | 76 | darkModeMeidaQuery.addListener(updateForDarkModeChange); 77 | updateForDarkModeChange(); 78 | ``` 79 | 80 | 次のようなコードを適用して、ダークモードのサポートに成功しました。 81 | 82 | ```css 83 | @media (prefers-color-scheme: dark) { 84 | body { 85 | background: #323232; 86 | color: #fff; 87 | } 88 | 89 | header { 90 | background: #111; 91 | } 92 | 93 | footer { 94 | background: #111; 95 | } 96 | } 97 | ``` 98 | 99 | ![다크모드](img/dark-diff.png) 100 | 101 | ダークモードに関連するCSSコードは全て一つのメディアクエリーの中に作成されています。なので、ダークモードのCSSコードは`dark.css`というファイルに別々に保存しておき、`link`タグの`media`属性を活用してファイル単位のメディアクエリーを操作することができます。 102 | 103 | #### dark.css 104 | ```css 105 | /* 106 | media=(prefers-color-scheme: dark) 107 | */ 108 | 109 | body { 110 | background: #323232; 111 | color: #fff; 112 | } 113 | 114 | header { 115 | background: #111; 116 | } 117 | 118 | footer { 119 | background: #111; 120 | } 121 | ``` 122 | 123 | #### index.html 124 | ```html 125 | ... 126 | 129 | ... 130 | ``` 131 | 132 | ## ダークモードのオン・オフ 133 | 上で言及したダークモード認識文法はiOS13以上、iPadOS、MacOS Mojave以上の一部のブラウザでしか動作しません。最新バージョンのアップル機器を使わなかったり、prefers-color-schemeメディアクエリー機能をサポートしないブラウザを使用したりする場合は、ダークモードは動作しません。下記からはダークモード機能をサポートしない機器を使用するユーザーがダークモードを使用できるようにする方法を教えてあげます。 134 | 135 | 先にファイルを分離するために追加した`link`要素の`media`属性を操作することで、CSSを活性化したり、無効化したりすることができます。まず、ダークモードをオフ・オンにするJavaScript関数を`darkModeSwitch`と名付けて、これを操作するボタンを作ります。 136 | ```html 137 |

138 | Dark Mode: 139 | ON 140 | / 141 | OFF 142 |

143 | ``` 144 | 145 | そして、先に`dark.css`を読み込むために追加した`link`タグにidを付与します。 146 | ```html 147 | 151 | ``` 152 | 153 | 上記に言及した`ddarkModeSwitch`関数を定義します。ここで`link`要素の`media`属性を操作します。 154 | ```javascript 155 | function darkModeSwitch(status) { 156 | document 157 | .querySelector('#dark-mode-sheet') 158 | .setAttribute( 159 | 'media', 160 | status? 'screen' : 'not screen' 161 | ); 162 | } 163 | ``` 164 | 165 | ダークモードをオフにしてオンにする機能を追加しました。 しかし、ユーザーがウェブページをリフレッシュしたり、他のページに移動したりする場合、ダークモードは解除されます。 この問題は、Cookieを使ってブラウザに設定値を保存し、この設定に従ってウェブページを表示するようにすれば解決されます。 166 | 167 | この文では、クッキーの操作のために`JavaScript Cookie`ライブラリを使用します。 168 | [もっと見る](https://github.com/js-cookie/js-cookie) 169 | 170 | まず、ダークモードに変更するたびに、これをクッキーとして保存します。 クッキーは文字列で値を保存するため、状態値を整数型に変換して保存します。そして、この値を持ってくる関数`isDarkMode`も追加します。 171 | ```javascript 172 | function isDarkMode() { 173 | return Cookies.get('darkmode'); 174 | } 175 | 176 | function darkModeSwitch(status) { 177 | Cookies.set('darkmode', +status); 178 | document 179 | .querySelector('#dark-mode-sheet') 180 | //中略 181 | } 182 | ``` 183 | 184 | これからはウェブページが始まるときにこの値にしたがってページを操作しなければなりません。 クッキー値がある場合は、ユーザの機器のダークモード設定を無視し、クッキー値にしたがってページを操作します。もしクッキー値がなかったら何の操作もしません。保存された値は定数で成り立つ文字列なので、これを再び整数型に変更します。 185 | 186 | ```javascript 187 | //前略 188 | document.addEventListener('DOMContentLoaded', function () { 189 | const isDm = isDarkMode(); 190 | if (isDm != null) darkModeSwitch(+isDm); 191 | }); 192 | ``` 193 | 194 | ダークモードをすべてのユーザーに完璧にサポートできるようになりました。 195 | 196 | ## アニメーション 197 | ダークモードに変わるたびにアニメーションを追加すると、もっと完成度の高いウェブデザインになります。 198 | 199 | ![애니메이션](img/ani.gif) 200 | ```css 201 | body { 202 | transition: .5s background, .5s color; 203 | } 204 | 205 | header { 206 | transition: .5s background; 207 | } 208 | 209 | footer { 210 | transition: .5s background; 211 | } 212 | ``` 213 | 214 | テストに使用されたページは [こちら](https://tmdgus0084.github.io/apple-dark-mode/)をクリックして確認できます。様々なユーザーの経験のために、多くの資料を探索し、発展することを祈ります。この文が役に立ったならスター⭐️をクリックして下さい! 215 | 読んで頂いてありがとうございます. 216 | 217 | ## 寄与者 218 | - [Seung-hyun Hwang@tmdgus0084](https://github.com/tmdgus0084): チーフ・ディレクター 219 | - [Hyunwoo Park@lqez](https://github.com/lqez): 誤文訂正 220 | - [@Bgh0602](https://github.com/Bgh0602): 英語翻訳 221 | - [@sjbhm18](https://github.com/sjbhm18): 韓国語のバージョンの文書を校正して、これを英語と日本語に翻訳。 222 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌙 다크모드 웹 페이지 디자인 2 | 3 | - [한국어](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.md) 4 | - [English](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.en.md) 5 | - [日本語](https://github.com/tmdgus0084/apple-dark-mode/blob/master/README.jp.md) 6 | 7 | ## 목차 8 | 1. [다크모드란?](#다크모드란) 9 | 2. [왜 다크모드를 사용해야할까?](#왜-다크모드를-사용해야-할까) 10 | 3. [다크모드 인식하기](#다크모드-인식하기) 11 | 4. [다크모드 끄고 켜기](#다크모드-끄고-켜기) 12 | 5. [애니메이션](#애니메이션) 13 | 14 | ## 다크모드란? 15 | 2019년 9월 20일 새벽 2시, 애플은 아이폰과 아이팟 터치를 위한 새로운 운영체제인 iOS 13를 배포하기 시작했습니다. 또한 같은 해 6월 초에 열렸던 WWDC2019(애플 세계 개발자 회의)에서는, 웹개발자가 iOS13과 MacOS Mojave를 위한 다크모드를 제어할 수 있는 방법을 제공했습니다. 흰 바탕에 검은 글씨를 띄우던 기존의 UI와는 달리, 다크모드는 검은 바탕에 흰 글씨를 띄우며 전체적으로 어두운 모습을 보여주는 UI입니다. 16 | 17 | ## 왜 다크모드를 사용해야 할까? 18 | 19 | 우리가 다크모드를 사용해야 할 이유는 무엇일까요? 요즘 유행이라서? 멋져 보여서? 이보다 더 근본적인 이유가 있습니다. 20 | 21 | 웹페이지의 가장 중요한 목적은 웹 페이지의 정보를 사용자에게 명확하게 전달하는 것입니다. 영화관 직원들은 영화가 시작되기 전에 가장 먼저 상영관의 조명을 꺼서 주변을 어둡게 만듭니다. 그 이유는 주변을 꾸미는 요소가 너무 화려하거나 눈에 띄면 중심 콘텐츠에 대한 집중도는 외부로 분산되기 쉽기 때문입니다. 영화관 사례와 마찬가지로, 다크모드를 비롯한 여러 어두운 환경은 주변 요소보다 중심 콘텐츠에 더욱 집중할 수 있도록 해줍니다. 요즘 자주 쓰이는 심플한 UI 디자인 패턴도 이와 동일한 맥락입니다. 22 | 23 | 개발자는 사용자의 장애 여부와 관계없이 모든 사용자가 편리하게 소프트웨어를 사용할 수 있도록 제공할 의무를 가지고 있습니다. 애플의 'VoiceOver'는 시각 장애인을 위한 애플의 대표적인 접근성 도구로, 많은 시각장애인들이 아이폰을 선택하는 이유가 되었습니다. 이밖에도 화면 전체를 반전시키는 '색 반전' 기능이 저시력자나 밝은 빛에 예민한 사용자들에게 많은 도움을 주었습니다. 24 | 25 | 다크모드 또한 저시력자나 밝은 빛에 예민한 사용자에게 눈의 피로를 덜어주는 효과를 주어 큰 도움이 됩니다. 사용자가 색 반전 기능을 켜고 다크모드 웹 페이지에 방문하면 다시 밝은 색이 되지 않을까라고 걱정할지도 모르겠지만, 그럴 필요 없습니다. 애플의 '스마트 반전' 기능은 상황에 따라 적절하게 화면을 반전시키는 기능으로, 이미 다크모드를 지원하고 있다면 화면을 반전시키지 않습니다. 26 | 27 | 한 사용자가 스마트폰의 어두운 잠금 화면을 풀고, 어두운 웹 브라우저를 켜고, 어두운 주소창에 주소를 입력해서 우리 사이트에 접속하고 있습니다. 앗, 기기 자체에서 지원하는 다크모드를 사용하던 이 사용자는 다크모드를 지원하지 않는 우리 사이트에 접속하는 순간 섬광탄을 맞은 느낌을 간접적으로 체험합니다. ~~의사 선생님, 눈이 안보여여~~ 우리 개발자는 사용자에게 통일성 있는 사용자 경험을 제공할 필요가 있습니다. 사용하던 환경과 동떨어진 환경의 UI는 사용자에게 거부감과 우리 사이트에서 한시라도 빨리 떠나고 싶다는 마음을 심어줄 것입니다. 28 | 29 | 이러한 이유로, 이 글에서는 다크모드를 사용 중인 사용자가 웹 사이트에 접속할 경우 자동으로 웹 사이트를 어두운 디자인으로 변경하는 방법을 다룹니다. 30 | 31 | 32 | ## 다크모드 인식하기 33 | 사용자가 웹 사이트에 접속한 순간 다크모드를 적용하려면 우선 사용자의 기기가 다크모드를 사용하고 있는지 확인해야 합니다. CSS와 JavaScript에서 이를 확인하는 방법은 다음과 같습니다. 34 | 35 | #### CSS 36 | CSS는 사용자 기기가 어떤 테마를 사용하는지 알려주는 `prefers-color-scheme`미디어쿼리를 지원합니다. `prefers-color-scheme`는 다음과 같은 값을 가질 수 있습니다. 37 | 38 | - no-preference: 테마를 알리지 않음. 39 | - light: 라이트모드를 사용중임. 40 | - dark: 다크모드를 사용중임. 41 | 42 | 따라서 아래와 같은 코드를 통해 다크모드를 사용중인 사용자에게만 작동하는 CSS 코드를 작성할 수 있습니다. 43 | ```css 44 | @media (prefers-color-scheme: dark) { 45 | body { 46 | background: black; 47 | color: white; 48 | } 49 | } 50 | ``` 51 | 52 | 그러나, 일부 브라우저는 `prefers-color-scheme`미디어쿼리를 지원하지 않거나 실제 사용자 기기의 시스템 테마 설정과는 다른 값을 가지고 있습니다. 53 | 54 | - ✅ : 지원함 55 | - ❌ : 지원하지 않거나 시스템 테마 설정과 다른 값을 가짐. 56 | 57 | | OS / Browser | Safari | Chrome | Firefox | Whale | 58 | | ------------- |:------:|:------:|:-------:|:-----:| 59 | | iOS |✅ |❌ |❌ |❌ | 60 | | iPadOS |✅ |❌ |❌ |❌ | 61 | | macOS |✅ |✅ |✅ |✅ | 62 | 63 | 64 | #### JavaScript 65 | JS에서는 CSS의 미디어쿼리를 빌려온 뒤, `prefers-color-scheme` 미디어쿼리 지원여부를 확인하여 호환성을 결정합니다. 66 | 67 | ```javascript 68 | const darkModeMeidaQuery = window.matchMedia('(prefers-color-scheme: dark)'); 69 | 70 | function updateForDarkModeChange() { 71 | if (darkModeMeidaQuery.matches) { 72 | // Dark mode is on. 73 | } else { 74 | // Dark mode is off. 75 | } 76 | } 77 | 78 | darkModeMeidaQuery.addListener(updateForDarkModeChange); 79 | updateForDarkModeChange(); 80 | ``` 81 | 82 | 이제 다음과 같은 코드를 적용해서 다크모드를 지원하는 데 성공했습니다. 83 | 84 | ```css 85 | @media (prefers-color-scheme: dark) { 86 | body { 87 | background: #323232; 88 | color: #fff; 89 | } 90 | 91 | header { 92 | background: #111; 93 | } 94 | 95 | footer { 96 | background: #111; 97 | } 98 | } 99 | ``` 100 | 101 | ![다크모드](img/dark-diff.png) 102 | 103 | 다크모드와 관련된 CSS 코드는 모두 하나의 미디어쿼리 속에 작성되어 있습니다. 따라서 다크모드의 CSS 코드는 `dark.css`라는 파일에 따로 담아두고 `link` 태그의 `media` 속성을 활용해 파일 단위의 미디어쿼리를 조작할 수 있습니다. 104 | 105 | #### dark.css 106 | ```css 107 | /* 108 | media=(prefers-color-scheme: dark) 109 | */ 110 | 111 | body { 112 | background: #323232; 113 | color: #fff; 114 | } 115 | 116 | header { 117 | background: #111; 118 | } 119 | 120 | footer { 121 | background: #111; 122 | } 123 | ``` 124 | 125 | #### index.html 126 | ```html 127 | ... 128 | 131 | ... 132 | ``` 133 | 134 | ## 다크모드 끄고 켜기 135 | 위에서 언급한 다크모드 인식 문법은 iOS13 이상, iPadOS, MacOS Mojave 이상의 일부 브라우저에서만 작동합니다. 최신 버전의 애플기기가 아니거나 prefers-color-scheme 미디어쿼리 기능을 지원하지 않는 브라우저를 사용하고 있다면 다크모드는 작동하지 않습니다. 아래부터는 다크모드 기능을 지원하지 않는 기기를 사용하는 이용자가 다크모드를 사용할 수 있도록 하는 방법을 다룹니다. 136 | 137 | 앞서 파일을 분리하기 위해 추가한  `link` 요소의  `media` 속성을 조작하는 것으로 해당 CSS를 활성화하거나 비활성화할 수 있습니다. 우선 다크모드를 끄고 켜는 JavaScript 함수를  `darkModeSwitch`라고 하고 이를 조작할 버튼을 만듭니다. 138 | ```html 139 |

140 | Dark Mode: 141 | ON 142 | / 143 | OFF 144 |

145 | ``` 146 | 147 | 그리고 위에서 `dark.css`를 불러오기 위해 추가한 `link` 태그에 id를 부여합니다. 148 | ```html 149 | 153 | ``` 154 | 155 | 위에서 언급한 `darkModeSwitch` 함수를 정의해줍니다. 여기서 `link` 요소의 `media` 속성을 조작할 것입니다. 156 | ```javascript 157 | function darkModeSwitch(status) { 158 | document 159 | .querySelector('#dark-mode-sheet') 160 | .setAttribute( 161 | 'media', 162 | status? 'screen' : 'not screen' 163 | ); 164 | } 165 | ``` 166 | 167 | 어렵지 않게 다크모드를 끄고 켜는 기능을 추가하였습니다. 하지만 사용자가 웹 페이지를 새로고침하거나 다른 페이지로 이동할 경우 다크모드는 해제됩니다. 이 문제는 Cookie를 사용해 브라우저에 설정값을 저장하고 이 설정에 따라 웹 페이지를 표시하도록 하면 해결됩니다. 168 | 169 | 이 글에서는 쿠키의 조작을 위해  `JavaScript Cookie` 라이브러리를 사용합니다.  170 | [자세히보기](https://github.com/js-cookie/js-cookie) 171 | 172 | 우선 다크모드로 변경할 때마다 이를 쿠키로 저장합니다. 쿠키는 문자열로 값을 저장하기 때문에 상태값을 정수형으로 변환해서 저장하였습니다. 그리고 이 값을 가져오는 함수 `isDarkMode`도 추가합니다. 173 | ```javascript 174 | function isDarkMode() { 175 | return Cookies.get('darkmode'); 176 | } 177 | 178 | function darkModeSwitch(status) { 179 | Cookies.set('darkmode', +status); 180 | document 181 | .querySelector('#dark-mode-sheet') 182 | //중략 183 | } 184 | ``` 185 | 186 | 이제 웹 페이지가 시작될 때 이 값에 따라 페이지를 조작해야 합니다. 쿠키값이 있다면 사용자 기기의 다크모드 설정을 무시하고 쿠키값에 따라 페이지를 조작합니다. 만약 쿠키값이 없다면 아무런 조작도 하지 않습니다. 저장된 값은 정수로 이루어진 문자열이므로 이를 다시 정수형으로 변경해줍니다. 187 | 188 | ```javascript 189 | //전략 190 | document.addEventListener('DOMContentLoaded', function () { 191 | const isDm = isDarkMode(); 192 | if (isDm != null) darkModeSwitch(+isDm); 193 | }); 194 | ``` 195 | 196 | 이제 다크모드를 모든 사용자에게 완벽하게 지원할 수 있게 됩니다. 197 | 198 | ## 애니메이션 199 | 다크모드로 변할 때 애니메이션을 추가하면 더욱 완성도 있는 웹 디자인이 됩니다. 200 | 201 | ![애니메이션](img/ani.gif) 202 | ```css 203 | body { 204 | transition: .5s background, .5s color; 205 | } 206 | 207 | header { 208 | transition: .5s background; 209 | } 210 | 211 | footer { 212 | transition: .5s background; 213 | } 214 | ``` 215 | 216 | 테스트에 사용된 페이지는 [여기](https://hsh2001.github.io/apple-dark-mode/)를 눌러 확인할 수 있습니다. 217 | 다양한 사용자의 사용자 경험을 위해 더 많은 자료를 탐색하고 발전하길 기원합니다. 218 | 이 글이 도움이 되었다면 스타⭐️를 눌러주세요! 219 | 감사합니다. 220 | 221 | ## 도움을 주신 분들 222 | - [Seung-hyun Hwang@hsh2001](https://github.com/hsh2001): 프로젝트 총괄 223 | - [Hyunwoo Park@lqez](https://github.com/lqez): 오탈자 수정 224 | - [@Bgh0602](https://github.com/Bgh0602): 영어 번역 225 | - [@sjbhm18](https://github.com/sjbhm18): 영어 / 일본어 번역 및 문장 개선 226 | -------------------------------------------------------------------------------- /assets/css/content.css: -------------------------------------------------------------------------------- 1 | body { 2 | transition: .5s background, .5s color; 3 | } 4 | 5 | header { 6 | transition: .5s background; 7 | position: sticky; 8 | left: 0; 9 | top: 0; 10 | width: 100vw; 11 | padding: 1.5rem 0; 12 | background: white; 13 | border-bottom: 2px solid #dcdcdc; 14 | font-size: 1.5rem; 15 | text-align: center; 16 | } 17 | 18 | nav { 19 | background: #000; 20 | color: #fff; 21 | padding: 1rem 0; 22 | } 23 | 24 | nav > div { 25 | width: 80%; 26 | max-width: 550px; 27 | margin: auto; 28 | display: flex; 29 | justify-content: space-between; 30 | } 31 | 32 | nav a { 33 | color: white; 34 | } 35 | 36 | article { 37 | width: 90%; 38 | max-width: 880px; 39 | margin: auto; 40 | } 41 | 42 | footer { 43 | transition: .5s background; 44 | padding: 1.5rem 0; 45 | background: #dcdcdc; 46 | text-align: center; 47 | } 48 | -------------------------------------------------------------------------------- /assets/css/dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | media=(prefers-color-scheme: dark) 3 | */ 4 | 5 | body { 6 | background: #323232; 7 | color: #fff; 8 | } 9 | 10 | header { 11 | background: #111; 12 | } 13 | 14 | footer { 15 | background: #111; 16 | } 17 | -------------------------------------------------------------------------------- /assets/css/reset.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Ubuntu', sans-serif; 3 | margin: 0; 4 | border: 0; 5 | padding: 0; 6 | outline: 0; 7 | } 8 | 9 | a { 10 | color: skyblue; 11 | } 12 | -------------------------------------------------------------------------------- /assets/js/dark.js: -------------------------------------------------------------------------------- 1 | function isDarkMode() { 2 | return Cookies.get('darkmode'); 3 | } 4 | 5 | function darkModeSwitch(status) { 6 | Cookies.set('darkmode', +status); 7 | document 8 | .querySelector('#dark-mode-sheet') 9 | .setAttribute( 10 | 'media', 11 | status? 'screen' : 'not screen' 12 | ); 13 | } 14 | 15 | document.addEventListener('DOMContentLoaded', function () { 16 | const isDm = isDarkMode(); 17 | if (isDm != null) darkModeSwitch(+isDm); 18 | }); 19 | -------------------------------------------------------------------------------- /assets/js/example.js: -------------------------------------------------------------------------------- 1 | const darkModedaiQuery = window.matchMedia('(prefers-color-scheme: dark)'); 2 | 3 | function updateForDarkModeChange() { 4 | if (darkModedaiQuery.matches) { 5 | // Dark mode is on. 6 | } else { 7 | // Dark mode is off. 8 | } 9 | } 10 | 11 | darkModedaiQuery.addListener(updateForDarkModeChange); 12 | updateForDarkModeChange(); 13 | -------------------------------------------------------------------------------- /img/ani.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsh2001/apple-dark-mode/536d19b2a7fba57487b6c3a7761dd25740e1752a/img/ani.gif -------------------------------------------------------------------------------- /img/dark-diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsh2001/apple-dark-mode/536d19b2a7fba57487b6c3a7761dd25740e1752a/img/dark-diff.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Apple dark mode test. 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | Dark Mode Test Page. 23 |
24 | 25 | 32 | 33 |
34 |

Hello world!

35 | 36 |

37 | Dark Mode: 38 | ON 39 | / 40 | OFF 41 |

42 | 43 |

44 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 45 |

46 |

47 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 48 |

49 |

50 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 51 |

52 |

53 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 54 |

55 |

56 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 57 |

58 |

59 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 60 |

61 |
62 | 63 | 71 | 72 | 73 | --------------------------------------------------------------------------------