基于规则的动态主题框架

在 12 月,我发布了 一篇介绍 Firefox 主题 API 的文章。虽然它允许你做很多事情,例如 动画主题macOS 样式的过度滚动交互式主题编辑器,但 API 有一些限制。与传统的 CSS 主题相比,动态主题 API 的一个问题是,它需要熟悉 JavaScript 和 WebExtension API 才能创建一个基本的动态主题。

为了解决这个问题,我尝试了一个简单的系统,它使用简单的主题“规则”来启用动态主题。“规则”由一个 JavaScript 条件字符串和一个主题名称组成。它在这里:https://github.com/nt1m/theming-rules

此样板代码处理了动态主题所需的所有繁重工作,例如设置 WebExtension 监听器,与其他 API 挂钩,以便让你专注于动态主题部分。

要开始使用,请在本地克隆上面的存储库。所有配置都位于样板代码的 config.js 文件中。打开此文件后,你应该会看到下面的代码

// The default theme: used when no rule is matched
const DEFAULT_THEME = "bright";

// The theming rules: priority is given to the bottom-most rule
const RULES = [
  ["privatebrowsing", "shady"],
  ["(hour >= 21) || (hour < 9)", "shady"]
];

在这种情况下,“shady”主题在晚上 9 点到早上 9 点以及私密标签页中使用。在所有其他情况下,使用“bright”默认主题。

让我们使用更复杂的示例编辑 config.js 文件

// The default theme: used when no rule is matched
const DEFAULT_THEME = "bright";

// The theming rules: priority is given to the bottom-most rule
const RULES = [
  ["privatebrowsing", "shady"],
  ["(hour >= 21) || (hour < 9)", "shady"],
  ["privatebrowsing && ((hour >= 21) || (hour < 9))", "bright"]
];

你能猜出这是做什么的吗?

这里有一个提示:规则具有“优先级”的概念,如果多个规则匹配,则使用最底部的规则。

在这种情况下,“bright”主题在白天和夜间私密窗口中使用。“shady”主题在夜间和白天私密窗口中使用。呼!

你可以使用多个属性,这是一个列表

  • inactive_window(非活动窗口)
  • privatebrowsing(隐私浏览)
  • container(容器)
  • domain(域名)
  • protocol(协议)
  • year(年份)
  • month(月份)
  • date(日期)
  • day(星期几)
  • hour(小时)
  • minutes(分钟)
  • seconds(秒)

稍后在本文中,我将展示如何定义自己的属性。

至于前面示例中使用的“bright”和“shady”主题,它们以标准的 WebExtension 主题格式定义,也在 config.js 文件中

const THEMES = {
  bright: {
    colors: {
      frame: "#dedede",
      tab_background_text: "#000",
      toolbar_text: "#000",
      toolbar: "#f8f8f8",
    }
  },
  shady: {
    colors: {
      frame: "#000",
      tab_background_text: "#ddd",
      toolbar_text: "#ccc",
      toolbar: "#3a3a3a"
    }
  }
};

用例:基于协议设置浏览器样式

现在你已经了解了这个系统,让我们构建一个有用的示例:让我们根据当前标签页是 HTTP 页面还是 HTTPS 页面来设置浏览器的样式。

首先,我们将定义 HTTP 和 HTTPS 主题的外观,我们将对 HTTP 站点使用红色,对 HTTPS 站点使用绿色

insecure: {
  colors: {
    frame: "red",
    tab_background_text: "black",
    toolbar: "pink",
    toolbar_text: "black"
  }
},
secure: {
  colors: {
    frame: "green",
    tab_background_text: "white",
    toolbar: "lightgreen",
    toolbar_text: "black"
  }
}

 

现在让我们定义规则

// The default theme: used when no rule is matched
const DEFAULT_THEME = "bright";
// The theming rules: priority is given to the bottom-most rule
const RULES = [
  ["protocol == 'http:'", "insecure"],
  ["protocol == 'https:'", "secure"]
];

…就是这样!请注意“bright”是如何作为默认主题保留的。这是因为存在其他协议(例如 file://),在这种情况下,扩展程序会应用中性主题。

示例如下所示

添加更多属性

你可能已经注意到了特殊的 privatebrowsinghourprotocol 关键字。它们是内置属性,并以此方式定义

privatebrowsing: {
  type: "boolean",
  async get(tab) {
    return tab.incognito;
  }
},
protocol: {
  type: "string",
  async get(tab) {
    return new URL(tab.url).protocol;
  }
},
hour: {
  type: "integer",
  async get(tab) {
    return (new Date(Date.now())).getHours();
  }
}

每个属性定义都采用 WebExtension Tab 对象(你可以选择忽略它),并从中返回一个值。

所有内置属性定义都可以在 properties.js 文件 中找到。

如果你想添加自己的属性怎么办?添加属性就像添加属性定义一样简单。这是一个使用 cookies API 返回当前标签页域名的 Cookie 数量的示例


cookies: {
  type: "integer",
  async get(tab) {
    let cookies = await browser.cookies.getAll({
      domain: new URL(tab.url).hostname
    });
    return cookies.length;
  }
}

它可以用来根据 Cookie 数量以不同的方式设置标签页的样式。

属性定义非常灵活,一些想法可能是拥有一个通过 XHR 返回当前天气的属性,或者页面上的跟踪器数量。唯一的限制是你的想象力 :)

试一试!

无论你需要一种非常简单的方法来创建不需要深入了解 WebExtension API 的动态主题,还是仅仅一种快速原型化上下文 UI 功能的方法,这都是适合你的工具!

使用此系统有很多可能性,看看你能想出什么将会很棒。

这是存储库:https://github.com/nt1m/theming-rules

欢迎在下面的评论中或通过 Github 问题分享你的经验。

编辑(2019 年 7 月 21 日):更新了代码示例以删除 已弃用的主题属性

关于 Tim Nguyen

我从事 Web 浏览器的工作。

更多 Tim Nguyen 的文章…


2 条评论

  1. djordge

    非常强大。我以前从未见过这样的应用程序。非常快速和简单。非常感谢你的智慧。

    2018 年 1 月 30 日 06:01

  2. Alexandre Sherozia

    非常感谢。一种使网站 UX 更具吸引力的有趣方法

    2018 年 2 月 1 日 23:01

本文的评论已关闭。