【CSS】2083- Tailwind輕松實現(xiàn)夜間模式,能跟隨系統(tǒng)又能手動控制!
共 14702字,需瀏覽 30分鐘
·
2024-06-23 10:32
作者:evanryuu 原文:https://juejin.cn/post/7312727134297210914
通過本文,你將會收獲到:
-
如何讓vscode不會再對 @tailwind,@apply之類的屬性報錯 -
如何讓你的應用既能跟隨系統(tǒng)設(shè)置,又能手動設(shè)置夜間模式
如果本文對你有所幫助,希望你能動動小手點個免費的贊,這會讓我更有動力進行寫作,謝謝你!
VSCode配置:
這一步可以讓你的 @apply 和 @tailwind 之類的 @ 屬性都不會報錯,并有提示
-
在項目根目錄下新建 .vscode文件夾 -
配置settings.json
{
"css.customData": [".vscode/tailwind.json"]
}
-
配置tailwind.json
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#tailwind"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply"
}
]
},
{
"name": "@responsive",
"description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#responsive"
}
]
},
{
"name": "@screen",
"description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#screen"
}
]
},
{
"name": "@variants",
"description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variants"
}
]
}
]
}
來源:github.com/tailwindlab…[1]
思路
方案1: 僅使用dark: 切換夜間模式
我們在 tailwind 中可以很輕松地靠類名去實現(xiàn)切換:
<div class="bg-white dark:bg-black">...</div>
但是我們并不希望每個地方都要我們?nèi)懀驗槿绻覀円O(shè)置n個屬性,就要寫2n個類名
<div class="bg-white dark:bg-black" />
所以會超出預想的長。
另外的問題是,如果我們將來希望去實現(xiàn)主題色的話,我們還要 bg-white dark:bg-black some-theme:bg-navy (假設(shè)有 some-theme: 這個東西),就更多了。
那有什么方法可以幫我們解決這個問題呢?
方案2: 利用 @apply 自定義類名實現(xiàn)切換
換一個思路,如果我們能有一個 bg-theme 這樣的類名,自動幫我們做這件事,是不是就解決了呢?
我們利用 @apply 的話就可以很簡單地寫出來下面的原子CSS:
.bg-theme {
@apply bg-white dark:bg-black;
}
哈!問題解決了…嗎?
要知道這樣寫出來的類名在vscode是沒有提示的,因為tailwind并不會自動去找css文件然后掃描里面的看起來像原子CSS的類名的,vscode 的tailwind插件是通過 tailwind.config. 文件工作的,只有在 tailwind.config. 里面有定義的東西才會有提示。
那我們可以這樣嗎?
// tailwind.config.js
module.exports = {
...
theme: {
extend: {
colors: {
theme: 'white dark:black'
}
}
}
...
}
很遺憾,dark:畢竟是類名而不是顏色值,因此tailwind并不支持這樣的寫法。
但是!Tailwind是支持CSS變量的。比如你可以寫成
// tailwind.config.js
...
colors: {
theme: 'var(--theme-color)'
}
...
有了這個功能,這里就可以引入第三個方案
方案3:媒體查詢 + CSS變量
我們知道如果用戶的系統(tǒng)設(shè)置為深色模式的話,我們是可以通過媒體查詢屬性 prefers-color-shceme: dark讀取到的,因此我們就能以此為根據(jù)寫出日間/夜間模式兩套變量
:root {
--theme-color: white;
}
@media (prefers-color-scheme: dark) {
:root {
--theme-color: black;
}
}
// tailwind.config.js
colors: {
theme: 'var(--theme-color)',
},
這樣寫出來的tailwind配置,不僅可以直接給bg / text / … 等等屬性使用,更重要的是,vscode這會兒有提示了!
這是一件非常棒的事情!并且,當我們使用css變量之后,我們要切換主題也很方便。
:root {
--blue-400: #4489f6;
--blue-500: #0070f3;
}
@media (prefers-color-scheme: dark) {
:root {
--blue-400: #2d79f0;
--blue-500: #063784;
}
}
// tailwind.config.js
colors: {
theme: 'var(--theme-color)',
blue: {
400: 'var(--blue-400)',
500: 'var(--blue-500)',
}
},
當然,你也可以不修改顏色,而是更改類名對應的色值
:root {
--theme-blue: #4489f6;
}
@media (prefers-color-scheme: dark) {
:root {
--theme-blue: #0070f3;
}
}
// tailwind.config.js
colors: {
theme: 'var(--theme-color)',
'theme-blue': 'var(--theme-blue)'
},
你還可以設(shè)置同名的屬性名分別給文本和背景
// tailwind.config.js
textColor: {
'theme': 'var(--theme-color-invert)'
},
backgroundColor: {
'theme': 'var(--theme-color)',
},
這個方案最大的缺點就是,本來只需要一個tailwind.config 的文件,現(xiàn)在還需要多個 css 文件(main.css / theme-light.css / theme-dark.css / …
即使我們可以使用一個入口main.css引入其余的css,這也會導致項目中需要多一行 import 'my-tailwind.css' 之類的文件,所以我個人認為仍然是有優(yōu)化空間的。
方案4: 類名 + 媒體查詢 + css變量
實際上,通過前面的流程,我們已經(jīng)可以比較完美地支持系統(tǒng)的深色模式了,但是有的時候我們的用戶可能只希望在我們的網(wǎng)站上開啟深色模式,所以我們要提供給用戶手動切換的能力。
這個時候dark就要以類名的形式添加在html上,而不是單純使用媒體查詢了,那我們可以大概思考一下我們的需求:
-
剛進入頁面時的夜間模式要以用戶的設(shè)置為準 -
用戶可以手動切換日間/夜間模式 -
用戶手動切換之后,仍然可以跟隨系統(tǒng)進行變化
由此,我們可以寫一個hook useDark 做下面的事情:
-
獲取當前系統(tǒng)的dark mode是否開啟,設(shè)為 systemDark,做useState默認值 -
const [dark, setDark] = useState<boolean>(systemDark) -
const toggleDark = () => setDark(!dark),用來手動切換日間/夜間模式 -
監(jiān)聽 mediaQuery ,讓 dark 跟隨用戶系統(tǒng)設(shè)定變化而變化 -
聲明并暴露 dark變量 以及 toggleDark 方法
最后我們的成果如下:
該 useDark 可以參考這里[2]
使用state導致的問題
如果有兩個組件A和B同時使用useDark的話,那么就可能會有bug。因為此時dark是組件內(nèi)部的state,這就導致了組件A點擊之后,只有組件A自己的dark變化了,組件B的dark是不會跟著變的。
如何解決這個問題,各位自由發(fā)揮就好了??localStorage,全局狀態(tài)管理,或者別的什么方法,都可以。
方案對比
至此,我們來對比下上述的幾個方法
| 方案對比 | 僅使用 dark: 選擇器 | @apply 自定義類名 | 媒體查詢+css變量 | 類名+監(jiān)聽媒體查詢+css變量 |
|---|---|---|---|---|
| 配置簡單 | ?????? | ???? | ???? | ?? |
| 開發(fā)友好 | ?? | ?? | ?????? | ?????? |
| 主題拓展 | ?? | ?? | ???? | ?????? |
| 適配性(系統(tǒng) / 用戶設(shè)置) | ???? | ???? | ???? | ?????? |
思考:如何實現(xiàn) useTheme?
我們現(xiàn)在是基于類名實現(xiàn)深色模式的,那我們其實也很容易切換主題。比如
:root {
--theme-color: white;
}
.dark {
--theme-color: black;
}
.violet {
--theme-color: violet;
}
只要我們對useDark稍作改動,就可以變?yōu)閡seTheme了!至于如何實現(xiàn),大家也可以自己嘗試。
總結(jié)
建議使用類名+監(jiān)聽媒體查詢+css變量的形式,除了剛開始的配置較為復雜,其余時候都很爽快。無論是開發(fā)使用的方便程度還是主題的拓展性。
由于我們項目中接入了 next-themes ,所以也省去了自己開發(fā) useDark ,畢竟它自帶了 useTheme 的鉤子。最后的解決方案就是
-
base.css 配置色值、字號等變量 -
theme-.css 配置各個主題下的設(shè)計類名 -
main.css 引入 -
tailwind.config. 配置別名
如果你覺得本文有錯誤,也歡迎你在評論區(qū)指出,我們可以友好討論??
https://github.com/tailwindlabs/tailwindcss/discussions/5258#discussioncomment-1979394: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Ftailwindlabs%2Ftailwindcss%2Fdiscussions%2F5258%23discussioncomment-1979394
[2]https://github.com/evanryuu/tw-dark-usecase/blob/main/src/hooks/useDark.ts: https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fevanryuu%2Ftw-dark-usecase%2Fblob%2Fmain%2Fsrc%2Fhooks%2FuseDark.ts
