hugo-book-theme 主题手动切换功能 的思路记录

交接的这几天,整理了一堆文档,正好都集中放在自己内部搭建的文档服务上。文档服务方案是 hugo 引擎 + book 主题。

晚上在暗黑主题的 VS Code 里写 MarkDown,切换到文档服务预览时,纯白色的 book 主题就很刺眼。要是文档也能暗黑模式就好了。采访了一圈组内同事,黑白喜好,一半一半。而我平时是习惯开着Mac系统的亮/暗自动切换,白天全亮,晚上全暗。眼睛确实好受一些。

最后两天闲来无事,那就想办法全都安排上吧。

设计

亮暗主题,本身 book 是都有的,但是只能在部署服务时,写死到配置文件里,要能自由切换得加个功能按钮。但是又不能破坏 book 主题自身的简洁。

那就先确定一下按钮风格:

  • 颜色尽量少
  • 线条尽量简

打个草稿: 图 10

按这个要求在图标库里,搜寻一番,一个极简的太阳,一个极简的月亮。下载 svg 矢量图备用。 亮/暗两种模式的图标有了,自动模式的图标找来找去没有心仪的。那就用sketch画一个吧。有先下好的 日月 svg ,原本想的简单组装一下就好。又一个 但是,日月本身都挺好看,放在一起就十分奇怪,可能是 比例不协调。

重新绘制路径、反复调整布局,直到看着比较协调舒适:(挺主观的个人标准。。。)

图 9

主题切换时的背景颜色会在黑白间变化,相应亮色主题 图标和文字 为黑色,暗色主题 图标和文字 为白色,自动模式的背景黑白都有可能,于是在自动模式的图标和文字都加入一点淡黄月色。

图 1

实现

自动模式标题 实现方式:按对角线左右各一个“auto”,通过 clip-path 来完成对角线切割,但是他俩拼接在一起时,总高度比原先差了一点点,最后又加入一个 正常的“auto” 来撑起高度。这样在文字内容切换时,不会有闪动。

为方便在浏览器内直接调试,先用内联格式来分别指定各自的样式:

<span class="dl_toggle-text-warp">
          <span style="
          position: relative;
          display: inline-block;
          ">
            <span style="
            display: inline-block;
            font-size: 1rem;
            clip-path: polygon(0% 0%, 100% 0%,100% 40%, 0% 70%);
            color: #fbe247a6;
            position: absolute;">
              auto
            </span>
            <span style="
            display: inline-block;
            font-size: 1rem;
            clip-path: polygon(100% 40%, 100% 100%, 0% 100%,0% 70%);
            color: #000;
            position: absolute;">
              auto
            </span>
            <span style="background-color: transparent;
            display: inline-block;
            font-size: 1rem;
            clip-path: polygon(100% 30%, 100% 100%, 0% 100%,0% 80%);
            color: transparent;
            ">
              auto
            </span>
          </span>
        </span>

切换功能,在js中定义一个简单的三态状态机,点击一次改变一次状态,在 light dark auto 间切换:

  // 初始化
  // 捕获 页面根元素 用以切换 主题 class
  var the_html = document.getElementsByTagName('html')[0]
  // 初始化按钮队列
  var dl_list = [];
  dl_list.push(document.getElementById("dl_toggle_light"))
  dl_list.push(document.getElementById("dl_toggle_auto"))
  dl_list.push(document.getElementById("dl_toggle_dark"))

  var dl_bt = document.getElementById("mode");

  var theme = {
    mode:null
  }
  // 读取 历史选择 的主题
  theme.mode = localStorage.getItem("theme.mode")===null?null:parseInt(localStorage.getItem("theme.mode"));
  // 默认主题
  if(theme.mode===null){
    theme.mode = 1
  }
  // 定义 状态机
  function toggle_mode(i){
    dl_list.forEach((element,index) => {
      if (index === i) {
        element.style.display = ''
      }
      else {
        element.style.display = 'none'
      }
    });

    switch (i) {
      case 0:
        the_html.className = 'light'
        break;
      case 1:
        the_html.className = ''
        break;
      case 2:
        the_html.className = 'dark'
        break;
    
      default:
        break;
    }
  }

  toggle_mode(theme.mode);
  // 调用 状态机
  function cl_bt (e) {
    theme.mode++;
    if(theme.mode > 2) {
      theme.mode = 0;
    }
      localStorage.setItem("theme.mode", theme.mode);
      toggle_mode(theme.mode)
  }

为记住用户的已选主题,将当前主题存入localStorage。其中有个注意点,localStorage 中的数据是以字符串格式存储的,取出时为方便计算显示转换为整数。parseInt(localStorage.getItem("theme.mode"))

效果:

单击按钮,手动指定 light亮/dark暗 文档主题,或者 在 auto自动 模式下,跟随系统主题。