mirror of
https://github.com/mdn/webextensions-examples.git
synced 2026-04-16 06:18:35 +02:00
Bug-2001318 example showing SVG icon reacting to theme (#620)
* Bug-2001318 example showing SVG icon reacting to theme * Updates for feedback
This commit is contained in:
@@ -548,6 +548,11 @@
|
|||||||
"javascript_apis": ["management.getAll", "management.setEnabled"],
|
"javascript_apis": ["management.getAll", "management.setEnabled"],
|
||||||
"name": "theme-switcher"
|
"name": "theme-switcher"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Demonstration of how to use the <code>prefers-color-scheme</code> media query to adapt an SVG icon to dark and light themes.",
|
||||||
|
"javascript_apis": ["runtime.getURL","tabs.query", "tabs.create", "tabs.update", "management.getAll","management.setEnabled","management.onEnabled", "pageAction.onClicked","action.onClicked"],
|
||||||
|
"name": "themed-icons"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "A collection of themes illustrating:<ul><li>weta_fade: a basic theme employing a single image specified in <code>theme_frame:</code>.</li><li>weta_fade_chrome: the weta_fade theme implemented with Chrome compatible manifest keys.</li><li>weta_tiled: a theme using a tiled image.</li><li>weta_mirror: a theme using multiple images and aligning those images in the header.</li><li>animated: use of an animated PNG.</li></ul>",
|
"description": "A collection of themes illustrating:<ul><li>weta_fade: a basic theme employing a single image specified in <code>theme_frame:</code>.</li><li>weta_fade_chrome: the weta_fade theme implemented with Chrome compatible manifest keys.</li><li>weta_tiled: a theme using a tiled image.</li><li>weta_mirror: a theme using multiple images and aligning those images in the header.</li><li>animated: use of an animated PNG.</li></ul>",
|
||||||
"javascript_apis": [],
|
"javascript_apis": [],
|
||||||
|
|||||||
27
themed-icons/README.md
Normal file
27
themed-icons/README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# themed-icons
|
||||||
|
|
||||||
|
This example demonstrates how to use the prefers-color-scheme media query to adapt an SVG icon to dark and light themes.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
This extension includes:
|
||||||
|
|
||||||
|
* a background script, `background.js`.
|
||||||
|
* page (address bar) and browser (toolbar) action icons.
|
||||||
|
|
||||||
|
The extension displays the [page action](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/pageAction) on any webpage, and the extension test page, which the extension opens when it starts, and the [browser action](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browserAction) is pinned to the toolbar when the extension is installed.
|
||||||
|
|
||||||
|
Clicking either icon opens the test extension page and switches to the next available theme.
|
||||||
|
|
||||||
|
When the active theme is enabled, the page and browser action change color based on the dark or light background color used in the UI.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
- When the built-in Firefox Alpenglow or dark themes are active, the icons are black with a white outline.
|
||||||
|
- When the built-in Firefox light theme is active, the icons are red with a black outline.
|
||||||
|
|
||||||
|
## NOTE: Implicit CSS filter applied to pageAction SVG icons in dark themes in Firefox Desktop 151 and earlier
|
||||||
|
|
||||||
|
In builds where the `about:config` preference `extensions.webextensions.pageActionIconDarkModeFilter.enabled` is set to `true` or not defined, a greyscale and brightness CSS filter is applied to page action icons for dark themes. This filter can reduce the contrast of icons that use multiple colors, see [Bug 2001318](https://bugzilla.mozilla.org/2001318).
|
||||||
|
|
||||||
|
This implicit CSS filter is turned off in Firefox Desktop Nightly in release 149 and later, and on the release channel
|
||||||
|
as part of [Bug 2016509](https://bugzilla.mozilla.org/2016509)
|
||||||
34
themed-icons/background.js
Normal file
34
themed-icons/background.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
async function openOrSelectTab(reloadExistingTab = false) {
|
||||||
|
const tabUrl = browser.runtime.getURL("/extpage.html");
|
||||||
|
let [tab] = await browser.tabs.query({ url: tabUrl });
|
||||||
|
if (!tab) {
|
||||||
|
await browser.tabs.create({ url: tabUrl, active: true });
|
||||||
|
} else {
|
||||||
|
await browser.tabs.update(tab.id, {
|
||||||
|
active: true,
|
||||||
|
...(reloadExistingTab ? { url: tabUrl } : {})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function switchToNextTheme() {
|
||||||
|
openOrSelectTab();
|
||||||
|
const themes = await browser.management.getAll().then(extensions => {
|
||||||
|
return extensions.filter(ext => ext.type === "theme");
|
||||||
|
});
|
||||||
|
const activeThemeIndex = themes.findIndex(theme => theme.enabled);
|
||||||
|
const nextThemeIndex = activeThemeIndex < themes.length - 1
|
||||||
|
? activeThemeIndex + 1
|
||||||
|
: 0
|
||||||
|
const nextTheme = themes[nextThemeIndex];
|
||||||
|
await browser.management.setEnabled(nextTheme.id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to the next theme available on pageAction or action icons clicks.
|
||||||
|
browser.pageAction.onClicked.addListener(switchToNextTheme);
|
||||||
|
browser.action.onClicked.addListener(switchToNextTheme);
|
||||||
|
|
||||||
|
// Open the the extension page in a new tab after the add-on is installed.
|
||||||
|
browser.runtime.onInstalled.addListener(() => {
|
||||||
|
openOrSelectTab(true);
|
||||||
|
});
|
||||||
32
themed-icons/extpage.html
Normal file
32
themed-icons/extpage.html
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>SVG themed icon using prefer-color-scheme media query</title>
|
||||||
|
<script defer="true" src="extpage.js"></script>
|
||||||
|
<style>
|
||||||
|
dt { font-weight: bold; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>SVG themed icon using prefer-color-scheme media query</h1>
|
||||||
|
<dl>
|
||||||
|
<dt>Active theme:</dt>
|
||||||
|
<dd id="active-theme">ACTIVE THEME INFO</dd>
|
||||||
|
</dl>
|
||||||
|
<h2>README.md</h2>
|
||||||
|
<pre id="read-me">README.md content</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
29
themed-icons/extpage.js
Normal file
29
themed-icons/extpage.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
let activeThemeEl = document.querySelector("#active-theme");
|
||||||
|
let readmeEl = document.querySelector("#read-me");
|
||||||
|
|
||||||
|
function updateThemeInfo(info) {
|
||||||
|
activeThemeEl.textContent = `${info.name} (${info.id})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include the README.md file content into the test page.
|
||||||
|
fetch("/README.md").then(r => r.text()).then(text => {
|
||||||
|
readmeEl.textContent = text;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set active theme info element content on page load.
|
||||||
|
browser.management.getAll().then(addons => {
|
||||||
|
updateThemeInfo(addons.find(addon => addon.type == "theme" && addon.enabled));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show pageAction icon on the extension page.
|
||||||
|
browser.tabs.getCurrent().then(tabInfo => {
|
||||||
|
browser.pageAction.show(tabInfo.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update active theme info when a theme is enabled.
|
||||||
|
browser.management.onEnabled.addListener(info => {
|
||||||
|
if (info.type !== "theme") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateThemeInfo(info);
|
||||||
|
});
|
||||||
37
themed-icons/manifest.json
Normal file
37
themed-icons/manifest.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
"description": "Adds page action and action SVG icons that adapt to dark and light themes using prefers-color-scheme media query.",
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "themed-icons",
|
||||||
|
"version": "1.0",
|
||||||
|
"homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/themed-icons",
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "themed-icons@mozilla.org",
|
||||||
|
"data_collection_permissions": {
|
||||||
|
"required": ["none"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"icons": { "32": "prefers-color-scheme-icon.svg" },
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"scripts": ["background.js"],
|
||||||
|
"type": "module"
|
||||||
|
},
|
||||||
|
|
||||||
|
"page_action": {
|
||||||
|
"default_icon": "prefers-color-scheme-icon.svg",
|
||||||
|
"show_matches": ["<all_urls>"]
|
||||||
|
},
|
||||||
|
|
||||||
|
"action": {
|
||||||
|
"default_icon": "prefers-color-scheme-icon.svg",
|
||||||
|
"default_area": "navbar"
|
||||||
|
},
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"management"
|
||||||
|
]
|
||||||
|
}
|
||||||
13
themed-icons/prefers-color-scheme-icon.svg
Normal file
13
themed-icons/prefers-color-scheme-icon.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
<style>
|
||||||
|
#outside { fill: black; }
|
||||||
|
#inside { fill: red; }
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
#outside { fill: white; }
|
||||||
|
#inside { fill: black; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<rect id="outside" width="16" height="16" />
|
||||||
|
<rect id="inside" x="4" y="4" width="8" height="8" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 384 B |
Reference in New Issue
Block a user