“像白天不懂夜的黑,像永恆燃燒的太陽,不懂那月亮的盈缺。” —— 黃桂蘭 0x00 大綱 0x01 前言 夜間模式(Dark Mode),也被稱為黑暗模式或深色模式,是一種高對比度,或者反色模式的顯示模式,這種模式現在越來越流行,因為和傳統的白底黑字相比,這種黑底白字的模式通常被認為可以緩解眼疲勞, ...
“像白天不懂夜的黑,像永恆燃燒的太陽,不懂那月亮的盈缺。” —— 黃桂蘭
0x00 大綱
目錄0x01 前言
夜間模式(Dark Mode),也被稱為黑暗模式或深色模式,是一種高對比度,或者反色模式的顯示模式,這種模式現在越來越流行,因為和傳統的白底黑字相比,這種黑底白字的模式通常被認為可以緩解眼疲勞,更易於閱讀。通過降低屏幕整體的亮度和使用暗色系的顏色,從而減小對眼睛的刺激。需要註意的是,夜間模式雖然能緩解視覺疲勞但並不能保護你的視力——該近視的還是會近視,該失眠的還是會失眠。
無論是 APP 還是網頁,它們的實現原理都是相同的,本質上都是顏色和樣式的替換,也就是主題的替換。下麵主要以網頁的夜間模式為例進行討論。
通常一個網頁的上的元素都有各自的顏色,例如文字的顏色,背景色,按鈕和邊框的顏色都有自己的單元設計,共同組合構成了一個整體的主題(配色方案)。一個網站可以包含相當多的 CSS, CSS 文件中的許多值都是重覆數據,更改這些顏色可能很困難且容易出錯,因為它(大概率)會分散在多個 CSS 文件中,並且可能不接受查找和替換。這將會變成開發人員的一個沉重負擔,即使是 Ctrl+C,Ctrl+V 。
0x02 CSS 自定義屬性
好在 CSS 規範裡面有自定義屬性(custom properties)的存在,它包含的值可以在整個文檔中重覆使用。通過自定義屬性與var()
函數的結合,可以達到一次更改,處處生效的效果。
通常的最佳實踐是定義在根偽類:root
下,這樣就可以在 HTML 文檔的任何地方訪問到它了。聲明一個自定義屬性,屬性名需要以兩個減號(--
)開始,屬性值則可以是任何有效的 CSS 值。
0x03 主題定義
首先以 CSS 自定義屬性的方式定義一套主題顏色作為我們的起點。
<!DOCTYPE html>
<html mode="light">
<head>
<meta charset="utf-8">
<style>
:root[mode="light"] {
--bg-color: #ffffff;
--text-color: #596172;
--border-color: #efafc7;
}
div {
background-color: var(--bg-color);
color: var(--text-color);
border: 4px solid var(--border-color);
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<div>Light or Dark.</div>
</body>
</html>
運行之後,應該得到這樣的效果:
註意
如果你沒有看到主題顏色生效,很可能是因為你的瀏覽器不支持 CSS 自定義屬性,請更換現代瀏覽器或者點擊這裡查看更多關於相容性的信息。
0x04 夜間模式
我們在上面的主題基礎上,增加一套夜間模式的樣式定義,同時增加一個按鈕,用來切換我們的主題樣式:
<!DOCTYPE html>
<html mode="light">
<head>
<meta charset="utf-8">
<style>
:root[mode="light"] {
--bg-color: #ffffff;
--text-color: #596172;
--border-color: #efafc7;
}
:root[mode="dark"] {
--bg-color: #202020;
--text-color: #d8d8d8;
--border-color: #d15900;
}
div {
background-color: var(--bg-color);
color: var(--text-color);
border: 4px solid var(--border-color);
width: 200px;
height: 200px;
}
</style>
<script>
function sw() {
var map = {'dark': 'light', 'light': 'dark'};
var current = document.querySelector('html').getAttribute('mode');
document.querySelector('html').setAttribute('mode', map[current]);
}
</script>
</head>
<body>
<div>Light Mode for Light.</div>
<button onclick="sw()">切換</button>
</body>
</html>
點擊按鈕,應該能看到切換後的效果:
這隻是一個簡單的示例,事實上,你可以細化和定義任何你想要的顏色,甚至是 SVG 等矢量圖元的顏色。
0x05 圖片的處理
一張白底黑字的圖片,在晚上看起來,就像是在直視一盞臺燈。通過 CSS 自定義屬性,我們已經完成了對各種頁面元素顏色的控制和改變,但是對於顏色既定的圖片(自帶背景色的圖片)來說,似乎就不怎麼行得通了。既然預處理行不通,就要求助於後處理了,沒錯,說的就是濾鏡(filter)。好在,濾鏡也是可以通過 CSS 自定義屬性定義的。
<!DOCTYPE html>
<html mode="dark">
<head>
<meta charset="utf-8">
<style>
div[mode="light"] {
--img-filter: brightness(1.0);
}
div[mode="dark"] {
--img-filter: brightness(.7);
}
img {
filter: var(--img-filter);
}
</style>
</head>
<body>
<div style="display: inline-block;" mode="light">
<img src="sun.jpg">
</div>
<div style="display: inline-block;" mode="dark">
<img src="sun.jpg">
</div>
</body>
</html>
運行後的效果如下,左邊是原圖,對應我們的正常模式,右邊是濾鏡處理後的圖片,對應我們的夜間模式:
可以看到圖片的整體亮度被降低,亮部不再顯得那麼刺眼,暗部則變得更暗。我們達到所需要的效果,但同時需要註意,它降低的是圖片中所有顏色分量的亮度,因此,如果濾鏡參數調得太低,將會導致原圖中部分細節的丟失或者使其上面的文字變得難易閱讀(這裡要吐槽下知乎的夜間模式)。個人建議的設置是保留70%~90%的亮度水平。
0x06 最後的拼圖
我們在上文處理了夜間模式的文字和圖片等文檔元素,但這樣真的完美了嗎?事實上並不。在圖片的處理中我們提到,可以通過降低圖片的整體亮度來進行柔化處理,使其在夜間顯得沒那麼刺眼。這種處理暗含一個條件就是,原始圖片的前景和背景本身是相對融洽的。
考慮有這樣一張圖片,它的內容是黑色的,背景是透明的。當它在背景色為白色的頁面上顯示,似乎效果很好。但當我們切換到夜間模式,此時頁面背景色變為黑色系,這張圖片顯然已經難以看清,極端情況下,當頁面的背景色和圖片的前景色相同時,這張圖片就像憑空消失了一樣。譬如下圖的二維碼:
解決這個問題的方法有很多,但我暫時沒有想到一個通用且完美的辦法:
- 設置兩套不同的圖片(成本巨大)
- 不使用透明的圖片(某些情況下不現實)
- 特殊問題特殊處理,給透明圖片賦予與全局背景色不同的背景顏色(適用於透明圖片不多的情況)
在探索這個問題的途中,倒是發現了另一種有趣的處理方法:
<!DOCTYPE html>
<html mode="dark">
<head>
<meta charset="utf-8">
<style>
div[mode="light"] {
--bg-color: #ffffff;
--img-filter: brightness(1.0);
}
div[mode="dark"] {
--bg-color: #090909;
--img-filter: brightness(.7);
}
div {
background-color: var(--bg-color);
}
img {
filter: var(--img-filter);
}
.special {
filter: var(--img-filter) invert(1);
}
</style>
</head>
<body>
<div style="display: inline-block;" mode="light">
<img src="qr.png">
</div>
<div style="display: inline-block;" mode="dark">
<img src="qr.png">
</div>
<div style="display: inline-block;" mode="dark">
<img src="qr.png" class="special">
</div>
</body>
</html>
這個方法適用於黑白圖片或者內容與顏色無強關聯的圖片,運行上面的代碼,我們會得到這樣的效果:
最右邊的圖片使用了完全翻轉濾鏡,也就是將黑的變白了,白的變黑了。這樣在白色背景下,我們看到的是正常的二維碼,在黑色背景下,看到的是反轉後的二維碼,並且還不會影響掃碼。
0x07 小結
我們通過 CSS 自定義屬性和濾鏡等特性完成了對夜間模式樣式的處理和優化,但是仍要註意在特定的場景下,會有混色的問題出現。如果混色的問題,最好的辦法還是特殊問題特殊處理,根據實際情況修改源圖或者使用翻轉濾鏡。暫時沒有完美的解決方案,期待隨著技術的發展,後續的 CSS 新特性能帶來新的工具和驚喜。