純CSS免費讓網站擁有暗黑模式切換功能

做者: JowayYoung
倉庫: GithubCodePen
博客: 官網掘金思否知乎
公衆號: IQ前端
特別聲明:原創不易,未經受權不得轉載或抄襲,如需轉載可聯繫筆者受權

前言

暗黑模式這個概念最先起源於MacOS系統Mojave,提供淺色主題深色主題兩種皮膚供用戶選擇,深色主題就是咱們常說的暗黑模式。爲了眼睛健康,筆者在手機、平板和電腦上都選擇了暗黑模式css

皮膚主題

隨着蘋果官方逐漸要求各大系統平臺都得適配暗黑模式,因此筆者也探索出一種應該是市面上最低成本網站暗黑模式適配方案html

認識筆者的朋友應該都知道筆者是一位重度CSS發燒友,固然此次也是使用純CSS實現這個方案。是的,不加任何一段JS,側面再次證實CSS的強大。前端

思路

思路很簡單,使用一個按鈕來回切換主題樣式。按鈕未選中則切換到淺色主題,按鈕選中則切換到深色主題。可用:checked+打輔助完成這個任務。ios

  • :checked:選項選中的表單元素
  • +:元素相鄰的同胞元素

使用<input>模擬按鈕,當按鈕處於選中狀態時觸發:checked,經過+帶動後面相鄰的網站主體<div>進入暗黑模式,選中狀態取消時則退出暗黑模式git

<body>
    <input class="ios-switch" type="checkbox">
    <div class="main">網站主體</div>
</body>

更多選擇器的功能和分類請回看筆者這篇文章《多是最全最易記的CSS選擇器分類大法》github

切換按鈕

打算設計一個美觀的按鈕,但是沒有特別思路,就打開iPhone,把設置裏的切換按鈕用純CSS實現一番。segmentfault

尺寸和顏色都是與iPhone切換按鈕一致。思路是使用<input>模擬按鈕,聲明appearance:none將其默認外觀抹去,使用::before模擬背景區域,使用::after模擬點擊區域,在觸發:checked後添加一些小動畫進行修飾,近乎完美地實現了iPhone切換按鈕瀏覽器

切換按鈕

<input class="ios-switch" type="checkbox">
.btn {
    border-radius: 31px;
    width: 102px;
    height: 62px;
    background-color: #e9e9eb;
}
.ios-switch {
    position: relative;
    appearance: none;
    cursor: pointer;
    transition: all 100ms;
    @extend .btn;
    &::before {
        position: absolute;
        content: "";
        transition: all 300ms cubic-bezier(.45, 1, .4, 1);
        @extend .btn;
    }
    &::after {
        position: absolute;
        left: 4px;
        top: 4px;
        border-radius: 27px;
        width: 54px;
        height: 54px;
        background-color: #fff;
        box-shadow: 1px 1px 5px rgba(#000, .3);
        content: "";
        transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
    }
    &:checked {
        background-color: #5eb662;
        &::before {
            transform: scale(0);
        }
        &::after {
            transform: translateX(40px);
        }
    }
}

請戳這裏查看在線演示與源碼。sass

暗黑模式

還記得4月4日那次全網開啓悼念模式嗎?筆者發表了一篇文章《一行代碼全站進入悼念模式》,巧妙地使用filter這個強大的CSS屬性。app

html {
    filter:grayscale(1);
}

真的是一行代碼,本次也不例外,一行代碼全站進入暗黑模式

html {
    filter: invert(1) hue-rotate(180deg);
}

filter的兼容性不差,各位同窗可放心使用,還有一些細節地方需注意,本文就不重複講解了,詳情可回看《一行代碼全站進入悼念模式》

filter是一個很是神奇的屬性,能將Photoshop一些基礎的濾鏡效果應用到網站上。筆者平時很是喜歡使用filter,在筆者的CodePen上有許多純CSS特效都使用了filter,細心的同窗可能會發現筆者特別喜歡使用hue-rotate()這個函數結合CSS變量動態生成過渡顏色,詳情請回看《妙用CSS變量,讓你的CSS變得更心動》

本次的暗黑模式使用到兩個濾鏡函數:invert()hue-rotate()

  • invert():反相,反向輸出圖像着色,值爲0%則無變化,值爲0~100%則是線性乘子效果,值爲100%則徹底反轉
  • hue-rotate():色相旋轉,減弱圖像着色,處理非黑白的顏色,值爲0deg則無變化,值爲0~360deg則逐漸減弱,值超過360deg則至關繞N圈再計算剩餘的值

invert()簡單理解就是黑變白,白變黑,黑白顛倒hue-rotate()簡單理解就是沖淡顏色。爲了確保主題色調不會改變,將色相旋轉聲明爲180deg比較合理。

依據上述分析的思路,當按鈕處於選中狀態時使用+連帶後面的同胞元素也進入選中狀態。若同胞元素無背景色需聲明background-color:#fff,不然沒法讓濾鏡效果起效,爲了讓這個同胞元素在使用濾鏡效果時過渡得更天然,聲明transition:all 300ms

.ios-switch {
    ...
    &:checked {
        ...
        & + .main {
            filter: invert(1) hue-rotate(180deg);
        }
    }
}
.main {
    background-color: #fff;
    transition: all 300ms;
}

CodePen上爲了更好地展現效果,就使用<iframe>引入咱們最愛的掘金社區,免費爲其增長暗黑模式切換功能😉。同時在排版上作了少許修改,請戳這裏查看在線演示與源碼。

暗黑模式

優化

細心的同窗可能會發現,怎麼圖片都變成照B超的感受了。

缺陷

按照設計原則來講,換膚只針對組件,像一些媒體類型的元素,例如背景、圖片、視頻等,都是不能直接處理的,需保持其原樣。既然暗黑模式是使用了濾鏡的反相和色相旋轉實現,那麼對這些媒體元素再次使用濾鏡的反相和色相旋轉就能復原了。使用過Photoshop濾鏡的同窗應該會更清楚。

img,
video {
    filter: invert(1) hue-rotate(180deg);
}

還有一個問題,背景怎樣處理?衆所周知,背景是使用background系列屬性進行聲明的,所以沒法經過特定的選擇器進行標註。可是,可換種思路處理,就是給有背景的元素加上一個特定類名,將其包含到上述規則裏便可。

經過Chrome DevTools查看掘金社區的網站源碼,發現這些頭像、縮略圖和展現圖都有一些特定類名,將其特定類名添加到規則裏。

img,
video,
.avatar,
.image,
.thumb {
    filter: invert(1) hue-rotate(180deg);
}

在通用網站裏,這個類名可自行定義,最可行的方法就是定義一個特定類名.exclude。不使用濾鏡效果的元素通通加上.exclude

.exclude {
    filter: invert(1) hue-rotate(180deg);
}

改造

上述爲了方便演示代碼,在CodePen Demo裏使用<iframe>引入咱們最愛的掘金社區。因爲沒法對<iframe>進行樣式聲明,因此轉移到掘金社區上,經過Chrome DevTools直接改造。

Chrome瀏覽器裏按F12Cmd+Alt+I打開Chrome DevTools,分析網站的HTML結構。

<body>
    <div id="__nuxt">...</div>
</body>

<body>裏插入切換按鈕

<body>
    <input class="ios-switch" type="checkbox">
    <div id="__nuxt">...</div>
</body>

把如下SCSS代碼轉換成CSS代碼插入到<head>新建的<style>裏。推薦一個在線SASS轉CSS工具網站,複製如下代碼到網站裏直接轉換,完成後再貼到<style>裏。

.btn {
    border-radius: 31px;
    width: 102px;
    height: 62px;
    background-color: #e9e9eb;
}
.ios-switch {
    position: relative;
    appearance: none;
    cursor: pointer;
    transition: all 100ms;
    @extend .btn;
    &::before {
        position: absolute;
        content: "";
        transition: all 300ms cubic-bezier(.45, 1, .4, 1);
        @extend .btn;
    }
    &::after {
        position: absolute;
        left: 4px;
        top: 4px;
        border-radius: 27px;
        width: 54px;
        height: 54px;
        background-color: #fff;
        box-shadow: 1px 1px 5px rgba(#000, .3);
        content: "";
        transition: all 300ms cubic-bezier(.4, .4, .25, 1.35);
    }
    &:checked {
        background-color: #5eb662;
        &::before {
            transform: scale(0);
        }
        &::after {
            transform: translateX(40px);
        }
        & + #__nuxt {
            filter: invert(1) hue-rotate(180deg);
            img,
            video,
            .avatar,
            .image,
            .thumb {
                filter: invert(1) hue-rotate(180deg);
            }
        }
    }
}
#__nuxt {
    background-color: #fff;
    transition: all 300ms;
}

完成後發現切換按鈕沒有出現,可經過position:absolute將其絕對定位到想要顯示的位置。

.ios-switch {
    position: absolute;
    right: 0;
    top: 0;
    z-index: 99999;
    outline: none;
}

或在<div id="__nuxt">裏任意地方建立一個<label>,經過聲明<input class="ios-switch" id="toggle" hidden><label for="toggle">互相綁定,將<input>的觸發區域轉移到<label>上。具體怎樣實現,可參照筆者這個純CSS標籤導航欄實現。

若以爲講解有點亂,可稍做整理,三步完成上述操做。

打開掘金社區網站

F12Cmd+Alt+I打開Chrome DevTools

<head>裏插入 <style>

爲了方便複製粘貼,筆者將上述分析得出的CSS代碼進行壓縮。

<style>.btn,.ios-switch::before,.ios-switch{border-radius:31px;width:102px;height:62px;background-color:#e9e9eb;}.ios-switch{position:relative;appearance:none;cursor:pointer;transition:all 100ms;}.ios-switch::before{position:absolute;content:"";transition:all 300ms cubic-bezier(0.45,1,0.4,1);}.ios-switch::after{position:absolute;left:4px;top:4px;border-radius:27px;width:54px;height:54px;background-color:#fff;box-shadow:1px 1px 5px rgba(0,0,0,0.3);content:"";transition:all 300ms cubic-bezier(0.4,0.4,0.25,1.35);}.ios-switch:checked{background-color:#5eb662;}.ios-switch:checked::before{transform:scale(0);}.ios-switch:checked::after{transform:translateX(40px);}.ios-switch:checked + #__nuxt{filter:invert(1) hue-rotate(180deg);}.ios-switch:checked + #__nuxt img,.ios-switch:checked + #__nuxt video,.ios-switch:checked + #__nuxt .avatar,.ios-switch:checked + #__nuxt .image,.ios-switch:checked + #__nuxt .thumb{filter:invert(1) hue-rotate(180deg);}#__nuxt{background-color:#fff;transition:all 300ms;}.ios-switch{position:absolute;right:0;top:0;z-index:99999;outline:none;}</style>
<body>裏插入 <input>
<body>
    <input class="ios-switch" type="checkbox">
    <div id="__nuxt">...</div>
</body>

就這樣,一個純CSS的實現方案就能讓網站瞬間擁有暗黑模式切換功能,有沒有又對CSS另眼相看了。

三步操做

總結

整個純CSS實現方案圍繞着:checked+filter三個點進行,缺一不可。看似簡單,若不是經常使用CSS作特效也很難想象出區區三個點打輔助也能完成一個這麼強大的功能。

<body>
    <input class="ios-switch" type="checkbox">
    <div class="main">網站主體</div>
</body>
.ios-switch {
    ...
    &:checked {
        ...
        & + .main {
            filter: invert(1) hue-rotate(180deg);
            img,
            video,
            .exclude {
                filter: invert(1) hue-rotate(180deg);
            }
        }
    }
}
.main {
    background-color: #fff;
    transition: all 300ms;
}

相比於CSS+JS實現方案而言,無需維護一整套暗黑模式樣式代碼,無需操做DOM,沒有了往常複雜的操做。除非要作一整套高度定製的暗黑模式才需一個CSS+JS實現方案,不然用該方案便可。

本方案具備如下特色。

  • 純CSS實現,簡單高效,逼格更高
  • 幾乎沒有維護成本,快速迭代
  • 充分利用濾鏡效果,兼容性好

試試無妨,完成了以爲效果不錯就趕忙找你老闆加薪去😜,哈哈!

對於筆者而言,CSS是一門天馬行空的語言,說它簡單也行說它困難也行。若你很喜歡CSS,想了解更多純CSS特效,可回看筆者往期的文章,也可瀏覽筆者我的官網的純CSS特效專輯,保證知足你的眼球。

最後,筆者很好奇咱們最愛的掘金社區會不會採納這種純CSS實現的換膚方案,各位掘友怎樣看呢?

結語

❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更多高質量文章

關注公衆號IQ前端,一個專一於CSS/JS開發技巧的前端公衆號,更多前端小乾貨等着你喔

  • 關注後回覆資料免費領取學習資料
  • 關注後回覆進羣拉你進技術交流羣
  • 歡迎關注IQ前端,更多CSS/JS開發技巧只在公衆號推送

相關文章
相關標籤/搜索