(S)CSS中实现主题样式的4½种方式

PM说要实现一个一键设置主题的功能,做为技术,你能想到的实现方式有哪些呢?

1. 什么是主题样式?

相信你们对网页的主题样式功能确定不陌生。对于一些站点,在基础样式上,开发者还会为用户提供多种主题样式以供选择。css

下面就是一个主题样式功能:用户能够在右侧选择本身喜欢的主题色,从而获得一个“个性”的页面。html

还有时候,咱们开发了一个系统用来售卖,采购咱们系统的客户可能有多个。也许其中一个客户很喜欢咱们当前的深色色系主题,可是另外一个系统的采购方但愿咱们能为它们定制一套新的样式。他们但愿买来的系统能贴合它们本身的品牌调性,变为浅色的。这其实也是一种主题样式的需求。浏览器

在上面的讨论里,除了“主题”外,咱们又引出了一个概念——个性化。常常,咱们说到主题时,还会有一种说法叫作:个性化主题。这二者在英文中分别有两个对应的词: Theming 与 Customisation。缓存

当咱们说主题(Theming)与个性化定制(Customisation)的时候,不少时候其实并无区分二者。但实际上,二者仍是有一些微妙的区别的。工具

1.1. 主题 Theming 与个性化定制 Customisation 的区别

咱们说的主题(Theming)与个性化定制(Customisation)的时候,仍是有一些微妙的区别的。字体

主题:由开发者定义

主要表如今:网站

  • 系统的输入是由开发者定义的
  • 通常来讲具备有限的种类
  • 具备已知的规则与常量

例如,咱们常见的一些应用会提供夜间主题、阅读模式,这些也算是主题(Theming)的范畴。spa

个性化定制:由用户定义

特色表如今:code

  • 系统的输入是由用户来提供
  • 通常具备无限种可能
  • 规则更灵活,用户“随心所欲”

能够看到,“个性化”其实更强调了用户对系统的的影响力。component

不少时候,咱们谈到“主题”与“个性化定制”时,也许并无一个明确的边界。从上面的描述也能够看出,二者彷佛是处于天平的两端,区别主要在于开发者对规则的控制力度以及所能实现的差别化的粒度。

而咱们更多的是在两点之间找到一个平衡点。

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

相关文章
相关标签/搜索