路客的文档

技术总结和生活记录

CSS变量+setProperty实现前端界面主体切换

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

在2022年了,前端页面主题切换的实现方法有很多,今天我来讲一下通过css变量+setProperty来实现主题切换。具体其他的方法就不讲了,有感兴趣的可以自行了解

前言

这篇文章主要介绍怎么实现主题切换,现在市面上基本上绝大部分web和app都支持主体切换,像微信啊,ios啊 都有什么深色模式,浅色模式。更有甚者,还有定制,可以自定义颜色。

市面上常见的换肤功能主要有以下2种:

  • 网站自带几套固定主题,用户只能选择有限的几个主题。
  • 主题色由用户随意更改,真正做到用户的个性化定制。

今天我通过css变量来实现换肤,再换肤之前,需要先讲解一下css变量。

CSS变量

CSS变量(官方称为自定义属性)是用户定义的值,它可以在你的代码库中设置一次并多次使用。它们使管理颜色、字体、大小和动画值变得更加容易,并确保整个web应用的一致性。

举个例子,你可以将品牌颜色设置为一个CSS属性(--primarycolor: #7232FA),并在任何使用品牌颜色的组件或样式中使用这个值(background: var(--primarycolor);)。

除了更加简洁以及不重复的代码,CSS变量可用于构建调色板,提高响应能力,并创建动态类型系统。

var()函数

var() 函数用于插入 CSS 变量的值。

CSS 变量可以访问 DOM,这意味着您可以创建具有局部或全局范围的变量,使用 JavaScript 来修改变量,以及基于媒体查询来修改变量。

使用 CSS 变量的一种好方法涉及设计的颜色。您可以将它们放在变量中,而不必一遍又一遍地复制和粘贴相同的颜色。

var() 如何工作

首先:CSS 变量可以有全局或局部作用域。

全局变量可以在整个文档中进行访问/使用,而局部变量只能在声明它的选择器内部使用。

如需创建具有全局作用域的变量,请在 :root 选择器中声明它。 :root 选择器匹配文档的根元素。

如需创建具有局部作用域的变量,请在将要使用它的选择器中声明它。

下面的例子与上面的例子相同,但是在这里我们使用 var() 函数。

首先,我们声明两个全局变量(--blue 和 --white)。然后,我们使用 var() 函数稍后在样式表中插入变量的值:

:root {
  --blue: #1e90ff;
  --white: #ffffff;
}

body { background-color: var(--blue); }

h2 { border-bottom: 2px solid var(--blue); }

.container {
  color: var(--blue);
  background-color: var(--white);
  padding: 15px;
}

button {
  background-color: var(--white);
  color: var(--blue);
  border: 1px solid var(--blue);
  padding: 5px;
}
复制代码

这样,当我们想要修改变量值,只需要修改一个地方就可以

前端换肤demo

介绍完css变量,接下来我们进入实战。先搭建一个基础的架子,有了好的架子才能实现换肤,先创建index.html文件

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./index.css">
  <script src="./index.js"></script>
  <link rel="stylesheet" href="../img/icon.svg">
</head>

<body>
  <!-- 头部 -->
  <header>
    <img class="logo" src="../img/D-logo.svg" alt="">
    <ul class="nav">
      <li>
        云居故里
      </li>
      <li>
        柚子无敌
      </li>
      <li>
        数据中心
      </li>
    </ul>
  </header>
  <!-- container -->
  <div class="container">
    <!-- 侧边栏 -->
    <aside>
      <ul class="sub_nav">
        <li>
          <img class="svg" src="../img/案例展示.svg">
          我的喜欢
        </li>
        <li>
          <img class="svg" src="../img/gongyizhanshi.svg">
          我的收藏
        </li>
        <li>
          <img class="svg" src="../img/guanzhugongzhonghao.svg">
          我的查看
        </li>
        <li>
          <img class="svg" src="../img/lunbobanner.svg">
          个人中心
        </li>
        <li>
          <img class="svg" src="../img/weiguanxinxi.svg">
          福利中心
        </li>
        <li>
          <img class="svg" src="../img/xuanfuanniu.svg">
          个人设置
        </li>
        <li>
          <img class="svg" src="../img/youhuiquan.svg">
          朋友推荐
        </li>
        <li>
          <img class="svg" src="../img/guanzhutiao.svg">
          朋友圈
        </li>
      </ul>
    </aside>
    <!-- 主体 -->
    <article>
      <div class="card_container">
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>

      </div>
      <button>+ 添加card</button>
    </article>
  </div>

</body>

</html>
复制代码

index.css文件

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-size: 16px;
}
s
head, body {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}
ul li {
  list-style: none;
  cursor: pointer;
}
.svg {
  width: 24px;
  height: 24px;
  margin-right: 4px;
}
header {
  display: flex;
  position: relative;
  align-items: center;
  height: 48px;
  width: 100%;
  font-size: 14px;
  color: #87898b;
  background-color: #fff;
  box-shadow: rgb(0 0 0 / 5%) 0px 4px 10px;
}
header .logo {
  width: 220px;
  height: 80%;
}
header .nav {
  display: flex;
  height: 100%;
  align-items: center;
}
header .nav li {
  position: relative;
  padding: 6px 12px;
  margin-right: 18px;
  font-size: 14px;
  background-color: #fff;
  border-radius: 1rem;
}
header .nav li:hover {
  color: #3375f1;
}
header .nav li::after {
  position: absolute;
  content: '';
  width: 100%;
  height: 2px;
  bottom: 0px;
  left: 0;
  background-color: #3375f1;
  transition: all .5s ease;
  transform: scale(0);
}
header .nav li:hover::after {
  transform: scale(1);
}
.container {
  display: flex;
  height: calc(100% - 60px);
}
.container aside {
  width: 220px;
  padding: 8px;
  height: 100%;
  background-color: #fafcff;
  border-right: 1px solid #e9ebee;
}
.container aside .sub_nav li {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 7px 20px;
  margin-bottom: 12px;
  font-size: 14px;
  border-radius: 4px;
}
.sub_nav li:hover {
  background-color: #ecf5fe;
}
.container article {
  flex: 1;
  height: 100%;
  margin: 20px 20px;
}
.card_container {
  display: flex;
}
article .card {
  cursor: pointer;
  width: 340px;
  height: 200px;
  font-size: 14px;
  border: 1px solid #ededee;
  margin-right: 40px;
  border-radius: 4px;
  transition: all .5s ease;
  /* box-shadow: rgb(0 0 0 / 5%) 0px 4px 10px; */
}
.card .header {
  color: #1d1f23;
  display: flex;
  font-size: 14px;
  font-weight: 700;
  align-items: center;
  padding: 0 16px;
  height: 56px;
  justify-content: space-between;
  border-bottom: 1px solid #ededee;
}
.card .header .more {
  font-weight: 400;
  color: #3375f1;
}
.card .body {
  padding: 10px 16px;
  font-size: 14px;
  color: #87898b;
}

article .card:hover {
  box-shadow: rgb(0 0 0 / 5%) 0px 4px 10px;
}
button {
  cursor: pointer;
  width: 96px;
  height: 32px;
  border: 0px;
  margin-top: 20px;
  border-radius: 4px;
  background-color: #3375f1;
  font-size: 14px;
  color: #fff;
}
复制代码

写好html和css后,页面大概长这样子

20221011163422.gif

基本页面搭建完后,我们要实现换肤,首先得有个button,就添加card这个按钮旁边吧

      <button>+ 添加card</button>
      <button>切换简约风格</button>
      <button>切换暗黑风格</button>
      <button>切换炫彩风格</button>
复制代码

按钮有了,接下来我们得把css里需要动态变化的量提取出来,换成var()

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- <link rel="stylesheet" href="./index.css"> -->
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-size: 16px;
    }

    :root {
      --theme_btn: #3375f1;
      --theme_shadow: rgb(0 0 0 / 5%) 0px 4px 10px;
      --theme_size_color: #87898b;
      --theme_card_bgc: #fff;
      --theme_aside_bgc: #fafcff;
      --theme_article_bgc: #fff;
      --theme_size: #000;
    }

    head,
    body {
      width: 100vw;
      height: 100vh;
      overflow: hidden;

    }

    ul li {
      list-style: none;
      cursor: pointer;
    }

    .svg {
      width: 24px;
      height: 24px;
      margin-right: 4px;
    }

    header {
      display: flex;
      position: relative;
      align-items: center;
      height: 48px;
      width: 100%;
      font-size: 14px;
      color: var(--theme_size_color);
      background-color: var(--theme_card_bgc);
      box-shadow: var(--theme_shadow);
    }

    header .logo {
      width: 220px;
      height: 80%;
    }

    header .nav {
      display: flex;
      height: 100%;
      align-items: center;
    }

    header .nav li {
      position: relative;
      padding: 6px 12px;
      margin-right: 18px;
      font-size: 14px;
      background-color: #fff;
      border-radius: 1rem;
    }

    header .nav li:hover {
      color: var(--theme_btn);
    }

    header .nav li::after {
      position: absolute;
      content: '';
      width: 100%;
      height: 2px;
      bottom: 0px;
      left: 0;
      background-color: var(--theme_btn);
      transition: all .5s ease;
      transform: scale(0);
    }

    header .nav li:hover::after {
      transform: scale(1);
    }

    .container {
      display: flex;
      height: calc(100% - 48px);
    }

    .container aside {
      width: 220px;
      padding: 8px;
      height: 100%;
      color: var(--theme_size);
      background-color: var(--theme_aside_bgc);
      border-right: 1px solid #e9ebee;
    }

    .container aside .sub_nav li {
      display: flex;
      align-items: center;
      width: 100%;
      padding: 7px 20px;
      margin-bottom: 12px;
      font-size: 14px;
      border-radius: 4px;
    }

    .sub_nav li:hover {
      background-color: #ecf5fe;
    }

    .container article {
      flex: 1;
      height: 100%;
      padding: 20px 20px;
      background-color: var(--theme_article_bgc);
    }

    .card_container {
      display: flex;
    }

    article .card {
      cursor: pointer;
      width: 340px;
      height: 200px;
      font-size: 14px;
      border: 1px solid #ededee;
      margin-right: 40px;
      border-radius: 4px;
      transition: all .5s ease;
      background-color: var(--theme_card_bgc);
      /* box-shadow: rgb(0 0 0 / 5%) 0px 4px 10px; */
    }

    .card .header {
      color: var(--theme_size);
      display: flex;
      font-size: 14px;
      font-weight: 700;
      align-items: center;
      padding: 0 16px;
      height: 56px;
      justify-content: space-between;
      border-bottom: 1px solid #ededee;
    }

    .card .header .more {
      font-weight: 400;
      color: var(--theme_btn);
    }

    .card .body {
      padding: 10px 16px;
      font-size: 14px;
      color: var(--theme_size_color);
    }

    article .card:hover {
      box-shadow: var(--theme_shadow);
    }

    button {
      cursor: pointer;
      width: 96px;
      height: 32px;
      border: 0px;
      margin-top: 20px;
      border-radius: 4px;
      background-color: var(--theme_btn);
      font-size: 14px;
      color: #fff;
    }
  </style>
  <script src="./index.js"></script>

</head>


<body>
  <!-- 头部 -->
  <header>
    <img class="logo" src="../img/D-logo.svg" alt="">
    <ul class="nav">
      <li>
        云居故里
      </li>
      <li>
        柚子无敌
      </li>
      <li>
        数据中心
      </li>
    </ul>
  </header>
  <!-- container -->
  <div class="container">
    <!-- 侧边栏 -->
    <aside>
      <ul class="sub_nav">
        <li>
          <img class="svg" src="../img/案例展示.svg">
          我的喜欢
        </li>
        <li>
          <img class="svg" src="../img/gongyizhanshi.svg">
          我的收藏
        </li>
        <li>
          <img class="svg" src="../img/guanzhugongzhonghao.svg">
          我的查看
        </li>
        <li>
          <img class="svg" src="../img/lunbobanner.svg">
          个人中心
        </li>
        <li>
          <img class="svg" src="../img/weiguanxinxi.svg">
          福利中心
        </li>
        <li>
          <img class="svg" src="../img/xuanfuanniu.svg">
          个人设置
        </li>
        <li>
          <img class="svg" src="../img/youhuiquan.svg">
          朋友推荐
        </li>
        <li>
          <img class="svg" src="../img/guanzhutiao.svg">
          朋友圈
        </li>
      </ul>
    </aside>
    <!-- 主体 -->
    <article>
      <div class="card_container">
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>
        <div class="card">
          <div class="header">
            <span>Yyou Design</span>
            <span class="more">更多</span>
          </div>
          <div class="body">Yyou Design是由互娱社区前端团队与 UED
            团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</div>
        </div>

      </div>
      <button>+ 添加card</button>
      <button id="common">切换简约风格</button>
      <button id="block">切换暗黑风格</button>
      <button id="colorful">切换炫彩风格</button>
    </article>
  </div>

</body>

</html>
复制代码

注意,这里我们把index.css的代码,全部通过内嵌放到了index.html中,这是因为后面如果用js修改变量的话,如果是link引入的样式,受游览器同源影响,是无法修改的,会导致报错。所以我们把css放到了html中

有了css变量,最后我们只需要写js代码,实现动态切换就可以了

// index.js
window.onload = function () {
  const commonBtn = document.getElementById('common');
  const blockBtn = document.getElementById('block');
  const colorfulBtn = document.getElementById('colorful');
  const ThemeSwitch = {
    common: {
      '--theme_btn': '#3375f1',
      '--theme_shadow': 'rgb(0 0 0 / 5 %) 0px 4px 10px',
      '--theme_size_color': '#87898b',
      '--theme_card_bgc': '#fff',
      '--theme_aside_bgc': '#fafcff',
      '--theme_article_bgc': '#fff',
      '--theme_size': '#000',
    },
    block: {
      '--theme_btn': 'purple',
      '--theme_shadow': 'rgb(255 255 255 / 20 %) 0px 4px 10px',
      '--theme_size_color': 'pink',
      '--theme_card_bgc': 'block',
      '--theme_aside_bgc': '#000',
      '--theme_article_bgc': '#000',
      '--theme_size': '#fff',
    },
    colorful: {
      '--theme_btn': 'skyblue',
      '--theme_shadow': 'rgb(0 0 0 / 5 %) 0px 4px 10px',
      '--theme_size_color': 'orange',
      '--theme_card_bgc': 'skyblue',
      '--theme_aside_bgc': 'orange',
      '--theme_article_bgc': 'pink',
      '--theme_size': '#000',
    },
  }
  console.log(document.body.style)
  commonBtn.addEventListener('click', (e) => {
    Object.keys(ThemeSwitch.common).reduce((acc, item) => {
      document.styleSheets[0].cssRules[0].style.setProperty(item, ThemeSwitch.common[item])

    }, [])
  })
  console.log(1)
  blockBtn.addEventListener('click', (e) => {
    Object.keys(ThemeSwitch.block).reduce((acc, item) => {
      document.styleSheets[0].cssRules[0].style.setProperty(item, ThemeSwitch.block[item])

    }, [])
  })
  colorfulBtn.addEventListener('click', () => {
    Object.keys(ThemeSwitch.colorful).reduce((acc, item) => {
      document.styleSheets[0].cssRules[0].style.setProperty(item, ThemeSwitch.colorful[item])
    }, [])
  })
}
复制代码

最后,让我们来看一下效果图

20221012160505.gif

大功告成,最后代码放码上掘金,一起来玩玩吧