(S)CSS中實現主題樣式的4½種方式 [譯]

原Slides: 4½ Methods for Theming in (S)CSS ; 做者: Harry Robertscss

PM說要實現一個一鍵設置主題的功能,做爲技術,你能想到的實現方式有哪些呢?html

1. 什麼是主題樣式?

相信你們對網頁的主題樣式功能確定不陌生。對於一些站點,在基礎樣式上,開發者還會爲用戶提供多種主題樣式以供選擇。瀏覽器

下面就是一個主題樣式功能:用戶能夠在右側選擇本身喜歡的主題色,從而獲得一個「個性」的頁面。緩存

還有時候,咱們開發了一個系統用來售賣,採購咱們系統的客戶可能有多個。也許其中一個客戶很喜歡咱們當前的深色色系主題,可是另外一個系統的採購方但願咱們能爲它們定製一套新的樣式。他們但願買來的系統能貼合它們本身的品牌調性,變爲淺色的。這其實也是一種主題樣式的需求。ide

在上面的討論裏,除了「主題」外,咱們又引出了一個概念——個性化。常常,咱們說到主題時,還會有一種說法叫作:個性化主題。這二者在英文中分別有兩個對應的詞: Theming 與 Customisation。工具

當咱們說主題(Theming)與個性化定製(Customisation)的時候,不少時候其實並無區分二者。但實際上,二者仍是有一些微妙的區別的。字體

1.1. 主題 Theming 與個性化定製 Customisation 的區別

咱們說的主題(Theming)與個性化定製(Customisation)的時候,仍是有一些微妙的區別的。網站

主題:由開發者定義

主要表如今:spa

  • 系統的輸入是由開發者定義的
  • 通常來講具備有限的種類
  • 具備已知的規則與常量

例如,咱們常見的一些應用會提供夜間主題、閱讀模式,這些也算是主題(Theming)的範疇。code

個性化定製:由用戶定義

特色表如今:

  • 系統的輸入是由用戶來提供
  • 通常具備無限種可能
  • 規則更靈活,用戶「隨心所欲」

能夠看到,「個性化」其實更強調了用戶對系統的的影響力。

不少時候,咱們談到「主題」與「個性化定製」時,也許並無一個明確的邊界。從上面的描述也能夠看出,二者彷佛是處於天平的兩端,區別主要在於開發者對規則的控制力度以及所能實現的差別化的粒度。

而咱們更多的是在兩點之間找到一個平衡點。

1.2. 對實現「主題功能」的建議

咱們已經對主題樣式有了初步的瞭解,若是你也在產品中遇到了主題樣式的相關需求,不妨先看看如下幾點建議:

  1. 儘量避免這個功能。由於不少時候這可能只是個僞需求。
  2. KISS原則(Keep It Simple, Stupid!)。儘量下降其複雜性。
  3. 儘可能只去改變外觀,而不要改動元素盒模型(box-model)。
  4. 嚴格控制你的規則,避免預期外的差別。
  5. 把它做爲一個錦上添花的功能來向上促銷(up-sell)。

2. 實現「主題樣式」的方式

2.1. 方式一:Theme Layer

Overriding default style with additional CSS.

這應該是實現主題功能的一種最經常使用的手段了。首先,咱們的站點會有一個最初的基礎樣式(或者叫默認樣式);而後經過添加一些後續的額外的CSS來覆蓋與從新定義部分樣式。

具體實現

首先,咱們引入基礎的樣式 components.* 文件

@import "components.tabs";
@import "components.buttons"
複製代碼

其中 components.tabs 文件內容以下

.tab {
    margin: 0;
    padding: 0;
    background-color: gray;
}
複製代碼

而後,假設咱們的某個主題的樣式文件存放於 theme.* 文件:

對應於 components.tabstheme.tabs 文件內容以下

.tab {
    background-color: red;
}
複製代碼

所以,咱們只須要引入主題樣式文件便可

@import "components.tabs";
@import "components.buttons"

@import "theme.tabs";
複製代碼

這樣當前的樣式就變爲了

.tab {
    margin: 0;
    padding: 0;
    /* background-color: gray; */
    background-color: red;
}
複製代碼

優勢

  • 實現方式簡單
  • 能夠實現將主題應用與全部元素

缺點

  • 過多的冗餘代碼
  • 許多的CSS實際上是無用的,浪費了帶寬
  • 把樣式文件切分到許多文件中,更加瑣碎

2.2. 方式二:Stateful Theming

Styling a UI based on a state or condition.

該方式能夠實現基於條件選擇不一樣的主題皮膚,並容許用戶在客戶端隨時切換主題。很是適合須要客戶端樣式切換功能,或者須要對站點某一部分(區域)進行獨立樣式設置的場景。

具體實現

仍是相似上一節中 Tab 的這個例子,咱們能夠將 Tab 部分的 (S)CSS 改成以下形式:

.tab {
    background-color: gray;
    
    .t-red & {
        background-color: red;
    }
    
    .t-blue & {
        background-color: blue;
    }
}
複製代碼

這裏咱們把.t-red.t-blue稱爲 Tab 元素的上下文環境(context)。Tab 元素會根據 context 的不一樣展現出不一樣的樣式。

最後咱們給body元素加上這個開關

<body class="t-red">
    <ul class="tabs">...</ul>
</body>
複製代碼

此時 Tab 的顏色爲紅色。

當咱們將t-red改成t-blue時,Tab 就變爲了藍色主題。

進一步的,咱們能夠建立一些 (S)CSS 的 util class(工具類)來專門控制一些 CSS 屬性,幫助咱們更好地控制主題。例如咱們使用以下的.u-color-current類來控制不一樣主題下的字體顏色

.u-color-current {
    .t-red & {
        color: red;
    }
    
    .t-blue & {
        color: blue;
    }
}
複製代碼

這樣,當咱們在不一樣主題上下文環境下使用.u-color-current時,就能夠控制元素展現出不一樣主題的字體顏色

<body class="t-red">
    <h1 class="page-title u-color-current">...</h1>
</body>
複製代碼

上面這段代碼會控制<h1>元素字體顏色爲紅色主題時的顏色。

優勢

  • 將許多主題放在了同一處代碼中
  • 很是適合主題切換的功能
  • 很是適合站點局部的主題化
  • 能夠實現將主題應用於全部元素

缺點

  • 有時有點也是缺點,將許多主題混雜在了同一塊代碼中
  • 可能會存在冗餘

2.3. 方式三:Config Theming

Invoking a theme based on settings.

這種方式實際上是在開發側來實現主題樣式的區分與切換的。基於不一樣的配置,配合一些開發的自動化工具,咱們能夠在開發時期根據配置文件,編譯生成不一樣主題的 CSS 文件。

它通常會結合使用一些 CSS 預處理器,能夠對不一樣的 UI 元素進行主題分離,而且向客戶端直接提供主題樣式下最終的 CSS。

具體實現

咱們仍是以 Sass 爲例:

首先會有一份 Sass 的配置文件,例如settings.config.scss,在這份配置中定義當前的主題值以及一些其餘變量

$theme: red;
複製代碼

而後對於一個 Tab 組件,咱們這麼來寫它的 Sass 文件

.tab {
    margin: 0;
    padding: 0;
    
    @if ($theme == red) {
        background-color: red;
    } @else {
        background-color: gray;
    }
}
複製代碼

這時,咱們在其以前引入相應的配置文件後

@import "settings.config";
@import "components.tabs";
複製代碼

Tab 組件就會呈現出紅色主題。

固然,咱們也能夠把咱們的settings.config.scss作的更健壯與易擴展一些

$config: (
    theme: red,
    env: dev,
)

// 從$config中獲取相應的配置變量
@function config($key) {
    @return map-get($config, $key);
}
複製代碼

與以前相比,這時候使用起來只須要進行一些小的修改,將直接使用theme變量改成調用config方法

.tab {
    margin: 0;
    padding: 0;
    
    @if (config(theme) == red) {
        background-color: red;
    } @else {
        background-color: gray;
    }
}
複製代碼

優勢

  • 訪問網站時,只會傳輸所需的 CSS,節省帶寬
  • 將主題的控制位置放在了一個地方(例如上例中的settings.config.scss文件)
  • 能夠實現將主題應用於全部元素

缺點

  • 在 Sass 中會有很是多邏輯代碼
  • 只支持有限數量的主題
  • 主題相關的信息會遍及代碼庫中
  • 添加一個新主題會很是費勁

2.4. 方式四:Theme Palettes

Holding entire themes in a palette file.

這種方式有些相似於咱們繪圖時,預設了一個調色板(palette),而後使用的顏色都從其中取出同樣。

在實現主題功能時,咱們也會有一個相似的「調色板」,其中定義了主題所須要的各類屬性值,以後再將這些信息注入到項目中。

當你常常須要爲客戶端提供徹底的定製化主題,而且常常但願更新或添加主題時,這種模式會是一個不錯的選擇。

具體實現

在方式三中,咱們在一個獨立的配置文件中設置了一些「環境」變量,來標示當前所處的主題。而在方式四中,咱們會更進一步,抽取出一個專門的 palette 文件,用於存放不一樣主題的變量信息。

例如,如今咱們有一個settings.palette.red.scss文件

$color: red;
$color-tabs-background: $color-red;
複製代碼

而後咱們的components.tabs.scss文件內容以下

.tabs {
    margin: 0;
    padding: 0;
    backgroung-color: $color-tabs-background;
}
複製代碼

這時候,咱們只須要引入這兩個文件便可

@import "settings.palette.red";
@import "components.tabs";
複製代碼

能夠看到,components.tabs.scss中並無關於主題的邏輯判斷,咱們只須要專一於編輯樣式,剩下就是選擇所需的主題調色板(palette)便可。

優勢

  • 編譯出來的樣式代碼無冗餘
  • 很是適合作一些定製化主題,例如一個公司採購了大家的系統,你能夠很方便實現一個該公司的主題
  • 能夠從一個文件中徹底重製出你須要的主題樣式

缺點

  • 因爲主要經過設定不一樣變量,因此代碼肯定後,能實現的修改範圍會是有限的

2.5. 方式五:用戶定製化 User Customisation

Letting users style their own UIs.

這種模式通常會提供一個個性化配置與管理界面,讓用戶能本身定義頁面的展現樣式。

「用戶定製化」在社交媒體產品、SaaS 平臺或者是 Brandable Software 中最爲常見。

具體實現

要實現定製化,能夠結合方式二中提到的 util class。

首先,頁面中支持自定義的元素會被預先添加 util class,例如 Tab 元素中的u-user-color-background

<ul class="tabs u-user-color-background">...</ul>
複製代碼

此時,u-user-color-background還並未定義任何樣式。而當用戶輸入了一個背景色時,咱們會建立一個<style>標籤,並將 hex 值注入其中

<style id="my-custom"> .u-user-color-background { background-color: #00ffff; } </style>
複製代碼

這時用戶就獲得了一個紅色的 Tab。

Twitter 就是使用這種方式來實現用戶定製化的界面樣式的:

優勢

  • 不須要開發人員的輸入信息(是用戶定義的)
  • 容許用戶擁有本身「獨一無二」的站點
  • 很是實用

缺點

  • 不須要開發人員的輸入信息也意味着你須要處理更多的「不可控」狀況
  • 會有許多的冗餘
  • 會浪費 CSS 的帶寬
  • 失去部分 CSS 的瀏覽器緩存能力

3. 如何選擇方案?

最後來聊聊方案的選擇。

在第二部分咱們已經瞭解了五種實現方式(或者說4½種方法,由於第五種其實更偏個性化定製一些),那麼面對產品需求,咱們應該如何選擇呢?

這裏有一個不是很是嚴謹的方式能夠參考。你能夠經過嘗試問本身下面這幾個問題來作出決定:

  • 是你仍是用戶誰來肯定樣式? 用戶:選擇【方式五】User Customisation

  • 主題是否會在客戶端中被切換? 是:選擇【方式二】Stateful Theming 或【方式五】User Customisation

  • 是否有主題能讓用戶切換? 是:選擇【方式二】Stateful Theming

  • 你是但願網站的某些部分須要有不一樣麼? 是:選擇【方式二】Stateful Theming

  • 是否有預設的主題讓客戶端來選擇? 是:選擇【方式三】Config Theming

  • 是不是相似「貼牌」這類場景? 是:選擇【方式一】Theme Layer 或【方式四】Theme Palettes

相關文章
相關標籤/搜索