在 Firefox 中使用新的主题 API

从功能强大的扩展程序,如 StratiformFT Deep Dark,到简单的 轻量级主题,主题在 Firefox 中一直很受欢迎。现在 Firefox Quantum (57) 已经发布,它带来了许多性能改进和闪亮的全新界面,我们希望通过一个新的主题 API 来弥合差距,该 API 允许你超越基本的轻量级主题。

演示者 John Gruen

你能主题化什么?

在 Quantum 发布之前,轻量级主题有一组有限的属性可以主题化:你只能添加一个标题图片并设置框架文本颜色和背景颜色。新的主题 API 引入了一些新属性。完整的列表可以在 MDN 上找到。一个基本的 Theme 对象如下所示

{
  "images": {
    "theme_frame": ""
  },
  "colors": {
    "frame": "tomato",
    "tab_background_text": "white",
    "toolbar": "#444",
    "toolbar_text": "lightgray",
    "toolbar_field": "black",
    "toolbar_field_text": "white"
  }
}

以下是上面主题的显示方式

注意 images.theme_frame 属性是如何被设置为一个空字符串的。这是因为它属于三个强制属性之一:images.theme_framecolors.framecolors.tab_background_text。(编辑:这些属性在最近的版本中已变为可选)

最后,轻量级主题的另一个改进是支持使用 images.additional_backgrounds 字段的多张标题图片,该字段接受一个图像路径数组。这些图像的对齐方式和平铺方式是通过 properties.additional_backgrounds_alignmentproperties.additional_backgrounds_tiling 实现的,它们分别接受一个 background-positionbackground-repeat 值数组。你可以查看 MDN 页面以获取示例。可以使用多个背景,以便在浏览器 UI 两侧显示窗帘,或者作为在 UI 中添加多个主题指示器(体育/天气/私人浏览)的一种方式。

动态主题

假设你想要在主题中引入夜间模式。动态主题允许你做到这一点。它们拥有普通浏览器扩展程序的全部功能。要使用动态主题,你需要在你的清单中添加 theme 权限

<a href="https://mdn.org.cn/en-US/docs/Mozilla/Add-ons/WebExtensions/API/theme/update">browser.theme.update()</a> 方法是这种主题的核心。它接受一个 Theme 对象作为参数。该方法可以在你的后台脚本中的任何位置调用。

对于这个例子,让我们创建一个扩展程序,根据是白天还是黑夜来切换主题。第一步是在你的后台脚本中创建一个函数,将你的主题切换到白天主题或黑夜主题

var currentTheme = '';

const themes = {
  'day': {
    images: {
      theme_frame: 'sun.jpg',
    },
    colors: {
      frame: '#CF723F',
      tab_background_text: '#111',
    }
  },
  'night': {
    images: {
      theme_frame: 'moon.jpg',
    },
    colors: {
      frame: '#000',
      tab_background_text: '#fff',
    }
  }
};

function setTheme(theme) {
  if (currentTheme === theme) {
    // No point in changing the theme if it has already been set.
    return;
  }
  currentTheme = theme;
  browser.theme.update(themes[theme]);
}

上面的代码定义了两个主题:白天主题和黑夜主题,setTheme 函数然后使用 browser.theme.update() 来设置主题。
下一步是使用这个 setTheme 函数,并定期检查扩展程序是否应该切换主题。你可以使用闹钟 API 来实现。下面的代码定期检查并相应地设置主题

function checkTime() {
  let date = new Date();
  let hours = date.getHours();
  // Will set the sun theme between 8am and 8pm.
  if (hours > 8 && hours < 20) {
    setTheme('day');
  } else {
    setTheme('night');
  }
}

// On start up, check the time to see what theme to show.
checkTime();

// Set up an alarm to check this regularly.
browser.alarms.onAlarm.addListener(checkTime);
browser.alarms.create('checkTime', {periodInMinutes: 5});

这就是这个例子!完整的示例可以在webextension-examples github 仓库中找到。

另一个例子中没有涵盖的方法是 <a href="https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/theme/reset">browser.theme.reset()</a>。这个方法只是将主题重置为默认的浏览器主题。

每个窗口的主题

动态主题 API 非常强大,但是如果你需要为私人窗口或非活动窗口应用不同的主题怎么办?从 Firefox 57 开始,就可以为 <a href="https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/theme/update">browser.theme.update()</a><a href="https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/theme/reset">browser.theme.reset()</a> 指定一个 windowId 参数。windowId 是由windows API 返回的相同 ID。

让我们做一个简单的例子,将一个深色主题添加到私人窗口,并将其他窗口设置为默认主题

我们首先定义 themeWindow 函数

function themeWindow(window) {
  // Check if the window is in private browsing
  if (window.incognito) {
    browser.theme.update(window.id, {
      colors: {
        frame: "black",
        tab_background_text: "white",
        toolbar: "#333",
        toolbar_text: "white"
      }
    });
  }
  // Reset to the default theme otherwise
  else {
    browser.theme.reset(window.id);
  }
}

完成之后,我们可以使用 windows API 将其连接起来

browser.windows.onCreated.addListener(themeWindow);

// Theme all currently open windows
browser.windows.getAll().then(wins => wins.forEach(themeWindow));

非常直观吧?完整的示例可以在这里找到。以下是该示例的外观

另一个使用这些功能的附加组件是 Jonathan Kingston 的 Containers 主题,它将每个窗口的主题设置为其所选标签的容器。该附加组件的源代码可以在这里找到

VivaldiFox 附加组件 也使用此功能在不同的窗口中显示不同的网站主题

获取有关当前主题的信息

从 Firefox 58 开始,你现在可以获取有关当前主题的信息并监视主题更新。以下是这为什么重要的原因

这允许附加组件将它们的界面与用户当前安装的主题无缝集成。一个例子是将你的侧边栏标签颜色与你当前主题的颜色相匹配。

为此,Firefox 58 提供了两个新的 API<a href="https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/theme/getCurrent">browser.theme.getCurrent()</a><a href="https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/theme/onUpdated">browser.theme.onUpdated</a>

以下是一个简单的示例,将一些当前主题属性应用于sidebar_action 的样式

function setSidebarStyle(theme) {
  const myElement = document.getElementById("myElement");

  // colors.frame and colors.accentcolor are aliases
  if (theme.colors && (theme.colors.accentcolor || theme.colors.frame)) {
    document.body.style.backgroundColor =
      theme.colors.accentcolor || theme.colors.frame;
  } else {
    document.body.style.backgroundColor = "white";
  }

  if (theme.colors && theme.colors.toolbar) {
    myElement.style.backgroundColor = theme.colors.toolbar;
  } else {
    myElement.style.backgroundColor = "#ebebeb";
  }
  
  if (theme.colors && theme.colors.toolbar_text) {
    myElement.style.color = theme.colors.toolbar_text;
  } else {
    myElement.style.color = "black";
  }
}

// Set the element style when the extension page loads
browser.theme.getCurrent().then(setSidebarStyle);

// Watch for theme updates
browser.theme.onUpdated.addListener(async ({ theme, windowId }) => {
  const sidebarWindow = await browser.windows.getCurrent();
  /*
    Only update theme if it applies to the window the sidebar is in.
    If a windowId is passed during an update, it means that the theme is applied to that specific window.
    Otherwise, the theme is applied globally to all windows.
  */
  if (!windowId || windowId == sidebarWindow.id) {
    setSidebarStyle(theme);
  }
});

完整的示例可以在Github 上找到。正如你在下面的截图中看到的那样,侧边栏使用了当前应用的浏览器主题的颜色

另一个例子是Tree Style Tab 附加组件,它使用了这些 API 来将它的界面与当前使用的主题集成。以下是一个演示该附加组件与 VivaldiFox 一起工作的视频

下一步是什么?

这个 API 还有更多功能!我们计划扩展支持的属性集,并完善主题应用方式周围的一些粗糙边缘。API 的跟踪错误可以在 Bugzilla 上找到

同时,我们迫不及待地想看看你能用新的主题 API 做些什么。请告诉我们你希望看到哪些改进。

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

关于 Tim Nguyen

我在 Web 浏览器方面工作。

更多 Tim Nguyen 的文章...


17 条评论

  1. w

    嗯,Firefox 看起来第一个截图 Stratiform 太棒了!而当前 Fx ui 是... 还有很多需要改进的地方... 而且它太白了!

    2017 年 12 月 4 日 上午 8:40

  2. Cocoroco

    目前还没有办法选择用户安装的主题,甚至没有办法选择默认主题(当它不是“当前”主题时),对吧?

    2017 年 12 月 4 日 上午 9:17

    1. Tim Nguyen

      您可以使用管理 API 列出主题名称并启用/禁用它们:https://mdn.org.cn/en-US/Add-ons/WebExtensions/API/management/getAll

      但获取主题属性的唯一方法是将其应用。

      有没有什么用例可以满足您访问禁用主题的属性的需求?

      2017 年 12 月 4 日 上午 10:40

      1. Cocoroco

        我设想的是同一个例子,为私密窗口使用一个特定的主题,但让用户可以在私密窗口中应用他们自己安装的主题。同样的想法可以扩展到夜间模式的例子...

        2017 年 12 月 4 日 上午 11:01

      2. Christian Kaindl

        这对主题预览很有用,这样插件就可以提供一个主题列表,每个主题都有一个小(或大)的 SVG 图像,其中包含该主题的颜色。这样用户就可以直观地看到主题的效果,并可以应用它。

        此致,
        Christian

        2017 年 12 月 5 日 上午 1:05

  3. Marcel

    当尝试更改工具栏内容时,我总是会收到“处理颜色错误:在 WebExtension 清单中发现了意外属性”的错误 :(
    我已经花了几个小时来处理它,但我只有在用 Javascript 执行操作时,或者只设置颜色中的两个必需变量(而不是可选变量)时,才能使其正常工作。
    有没有关于如何更改工具栏颜色的示例?我的意思是,一个完整的 manifest.json 文件,我可以以此为基础构建。我到处都找不到类似的东西。

    2017 年 12 月 4 日 下午 1:40

    1. Will

      像这样? https://pastebin.mozilla.org/9074052

      2017 年 12 月 4 日 下午 4:33

  4. Ken Saunders

    是否有计划(错误参考)来
    允许更改图标/按钮图像,更重要的是,更改大小?

    允许全局 UI 主题化(所有工具栏、菜单、对话框等)?
    至少是前景色和背景色。

    更像是一个功能而不是 API,但是将会有像主题字体和大小更改器(现在是 Vivaldi 的默认功能)这样的浏览器 UI 缩放功能吗?

    我知道这不是一个好的游说场所,但主要原因是
    辅助功能 - 视力障碍、视觉障碍、阅读障碍、认知障碍。
    对婴儿潮一代(他们占人口的很大一部分)很有帮助。
    对使用高分辨率设置和显示器的用户很有帮助。

    请随时将我的意见转达。

    立即行动,即可获得一年期的插件通讯订阅。

    2017 年 12 月 4 日 下午 1:52

    1. Tim Nguyen

      > 允许更改图标/按钮图像,更重要的是,更改大小?

      https://bugzilla.mozilla.org/show_bug.cgi?id=1348039

      > 允许全局 UI 主题化(所有工具栏、菜单、对话框等)?

      https://bugzilla.mozilla.org/show_bug.cgi?id=1417880
      https://bugzilla.mozilla.org/show_bug.cgi?id=1417883
      https://bugzilla.mozilla.org/show_bug.cgi?id=1418602

      > 更像是一个功能而不是 API,但是将会有像主题字体和大小更改器这样的浏览器 UI 缩放功能

      https://bugzilla.mozilla.org/show_bug.cgi?id=1347169

      2017 年 12 月 4 日 下午 3:51

      1. Ken Saunders

        太棒了!
        太激动了!
        很有希望!
        这里还有一个形容词 _________ 以及您的选择,是句号还是另一个感叹号。

        衷心感谢您抽出时间收集所有这些链接。

        在接下来的几个小时里,我将用我热情的咖啡来敬您一杯。
        然后我会用伏特加来庆祝,然后,嗯。
        我可能需要保释金。

        2017 年 12 月 5 日 下午 2:47

  5. Max

    渐变怎么样?我不喜欢扁平化设计,我想在“导航”和标签栏上制作一些渐变(不用 userChrome.css)。工具栏 API 怎么样?(我需要把一些扩展图标放到浏览器窗口的底部?)是否有关于它们的计划?

    2017 年 12 月 5 日 上午 1:10

    1. Gerd

      我不知道,但尽管不像原生渐变那样好,这个主题使用 PNG 来实现渐变效果:https://twitter.com/mart3ll/status/938131911817474049

      2017 年 12 月 6 日 上午 0:46

  6. Mauri

    您好,是否有计划让 browser.theme.getCurrent() 获取 Mozilla 提供的主题的信息?目前,它只返回扩展提供的主题的 Theme 对象。

    2017 年 12 月 5 日 下午 4:13

    1. Tim Nguyen

      计划最终将 Mozilla 提供的主题转换为 WebExtension 主题,这意味着 Light/Dark 主题将可以使用 theme.getCurrent()。

      2017 年 12 月 6 日 上午 0:52

      1. Matt

        AMO 上的主题怎么样?老实说,如果能够以编程方式判断一个主题是深色主题还是浅色主题,那就太好了。清单中有一个 theme_icons,但在有多组图标时它并不有用。

        2017 年 12 月 7 日 上午 11:43

        1. Tim Nguyen

          AMO 上的主题预计在添加 Android 支持后也会自动迁移到新的 WebExtension 格式。这意味着 theme.getCurrent() 在那里也会起作用。

          2017 年 12 月 7 日 下午 12:33

  7. Stu

    太棒了!这意味着像 colorfultabs 这样的东西可以回来了吗?

    还是说主题 API 还没有完全涵盖这一点?

    2017 年 12 月 10 日 下午 5:18

本文的评论已关闭。