Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension Icons for Light/Dark/Custom Theme #229

Open
hanguokai opened this issue Jun 13, 2022 · 35 comments
Open

Extension Icons for Light/Dark/Custom Theme #229

hanguokai opened this issue Jun 13, 2022 · 35 comments
Labels
enhancement Enhancement or change to an existing feature supportive: chrome Supportive from Chrome supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari

Comments

@hanguokai
Copy link
Member

hanguokai commented Jun 13, 2022

A little related to issue-216. Besides the text color and background color of the BadgeText, the extension icon itself is also a problem to adapt to various themes (Light/Dark/Custom).

Current state, developers can set static extension icon or dynamic change it by setIcon(). But:

  • Developers can't set different icons for different themes (at least in static way). So the icon is good for one theme, but may be illegible for another theme.
  • Single icon with transparent background is difficult to adapt to different color themes, unless you fill the background with a solid color.
  • To setIcon() dynamically, developers may need to know current background color of the browser toolbar to choose a color with sufficient contrast to draw the icon. Note: background color of browser toolbar may differ from the system theme, see this discussion.
@yankovichv
Copy link

I fully support it. However, it seems to me that it should be implemented as a browser-based JS API available for websites as well. The point is that all the problems from the list above are relevant for Favicons.

@hanguokai
Copy link
Member Author

website favicon vs extension icon

I think website favicon and extension icon are not exactly the same problem.

Here is a stackoverflow question for website favicon. There are several solution for it. For example, svg favicon with media query, <link> element with media attribute. At least, there is a no-JS solution for light/dart theme.

But these solutions do not apply to extensions. Extension icons are declared in manifest.json. And there is no window.matchMedia('xxx').addEventListener in extension service worker. There are also custom theme(extension theme) that can change toolbar color.

@dotproto
Copy link
Member

Chromium is tracking a similar feature request in issue 893175. A Chromium engineer shared a link to a design doc we were considering in comment 19, but that effort is currently on hold.

@xeenon, @zombie, and @mukul-p, it might be worth taking a look at this and sharing feedback either on the doc or on a related issue.

@hanguokai
Copy link
Member Author

I see the design doc. It is a static(declarative) way in manifest file. I think it solves 90% of the problems.

I think a runtime API should also be provided to solve dynamic problems, because developers can change icon by setIcon() method. For example adding two new apis below:

chrome.runtime.getTheme(); // return 'light' or 'dart' or background color
chrome.runtime.onThemeChanged.addListener(theme => changeIt()); // listener for theme changed

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Jun 23, 2022

We can draw some inspiration from ongoing discussion on how to handle this in PWA manifests, see:
w3c/manifest#758
w3c/manifest#975
w3c/image-resource#26

In general, going for a list of icons versus an object, like Mozilla did with theme_icons and PWA do with icons will give us more flexibility.

We can go for a syntax like this:

  "icons": [{
    "src": "/pwa-36.png",
    "sizes": "36x36",
    "media": "(prefers-color-scheme: dark)"
  }, {
    "src": "/pwa-adaptive-48.png",
    "sizes": "48x48",
    "purpose": "toolbar"
  }]

Or adding a color_scheme property. Which can be set to "light", "dark", and potentially "monospace" as many extension developers prefer to blend into the browsers design. This has the added benefit it's clear the browser will choose a dark-mode icon when the background of the browser team is a darker color even tho the OS is not using "dark mode".

@mixedpuppy
Copy link

Firefox implementation can be seen at e.g. https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/action

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Jul 7, 2022

For best backwards compatibility, we can consider implementing the theme_icons property for action/page_action/browser_action as is currently implemented by Firefox:

"action": {
  "browser_style": true,
  "default_icon": {
    "16": "button/geo-16.png",
    "32": "button/geo-32.png"
  },
  "default_title": "Whereami?",
  "default_popup": "popup/geo.html",
  "theme_icons": [{
    "light": "icons/geo-16-light.png",
    "dark": "icons/geo-16.png",
    "size": 16
  }, {
    "light": "icons/geo-32-light.png",
    "dark": "icons/geo-32.png",
    "size": 32
  }]
}

default_icon would continue to work fine. While theme_icons would take priority over default_icon if present. light would be used when light text and icons are needed. For example, with a darker background or with dark mode theme. While dark would be used for light mode themes.

We can then move forward with more breaking changes in regard to icons for Manifest v4.

@hanguokai
Copy link
Member Author

light would be used when light text and icons are needed. For example, with a darker background or with dark mode theme. While dark would be used for light mode themes.

I didn't know Firefox's theme_icons before. For me, it's counterintuitive. I prefer dark for icons used in dark theme and light for icons used in light theme.

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Aug 4, 2022

@hanguokai This would then be something we can fix once drafting MV4.

As for the API to update theme_icons, we can go for:

browser.action.setThemeIcons([{
    "light": "icons/geo-16-light.png",
    "dark": "icons/geo-16.png",
    "size": 16
  }, {
    "light": "icons/geo-32-light.png",
    "dark": "icons/geo-32.png",
    "size": 32
  }])

Calling this method would overwrite the theme_icons (if present) defined in the manifest file. If set to an empty array or null or undefined, resets the themeIcons to what has been defined in the manifest file or otherwise fallback to what is defined in 'default_icon'.

Currently in Firefox, setIcon will overwrite what is defined in theme_icons. Thus at least for MV2 and MV3 we seem to have to stick to this. This means setIcon would also overwrite setThemeIcons. This is not an issue considering developers can check the presence of setThemeIcons and use that instead of setIcon if present. Naturally setThemeIcons would overwrite the icon set by setIcon.

@xeenon
Copy link
Collaborator

xeenon commented Aug 4, 2022

The theme_icons key and browser.action.setThemeIcons sounds good to me for Safari.

@Rob--W
Copy link
Member

Rob--W commented Jan 25, 2023

Discussion on this issue has been narrowed to the action API, but the need for having a dark variant is broader than that. E.g. extension icons in chrome://extensions (Chrome) or about:addons (Firefox) are not toolbar buttons, but there is still a need to have dark theme support.

@Loirooriol
Copy link

For reference, 5 years ago I proposed browser.browserAction.setThemeIcons in https://bugzilla.mozilla.org/show_bug.cgi?id=1484840

@willdurand
Copy link

Chromium is tracking a similar feature request in issue 893175. A Chromium engineer shared a link to a design doc we were considering in comment 19, but that effort is currently on hold.

@xeenon, @zombie, and @mukul-p, it might be worth taking a look at this and sharing feedback either on the doc or on a related issue.

We (Mozilla) are going to add support for icons.dark and icons.light as well as action.default_icon.dark and action.default_icon.light in the manifest file (all versions probably). Here, dark and light refer to the current theme color scheme and not the text color like theme_icons like mentioned in some docs/links above.

As for theme_icons, we'll likely deprecate the manifest key in MV3+.

@sunxivincent
Copy link

any updates on chrome extension support for dark mode icon?

@Rob--W Rob--W added follow-up: chrome Needs a response from a Chrome representative supportive: firefox Supportive from Firefox labels Feb 16, 2023
@oliverdunk oliverdunk added supportive: chrome Supportive from Chrome and removed follow-up: chrome Needs a response from a Chrome representative labels May 11, 2023
@maxnikulin
Copy link

The title of this issue is specific to browser toolbar icons. Should another issue be created for contextMenus icons or should this issue be considered in wider sense?

A single context menu item by default uses the icons manifest key, but Firefox allows to specify per-item icon set, see menus.create().

Certainly dark menu background may require inverted colors for icons.

@hanguokai
Copy link
Member Author

This issue actually focuses on all the places where the icon appears, including:

  • browser toolbar
  • context menu
  • sidebar switcher
  • management page, e.g. chrome://extensions/
  • extension store?
  • ……

So I change the title of this issue (remove toolbar)

@hanguokai hanguokai changed the title Extension Icon Design for Light/Dark/Custom Theme on Browser Toolbar Extension Icons for Light/Dark/Custom Theme Jun 15, 2023
@hanguokai
Copy link
Member Author

I know that browser vendors have a lot of backlogs to do and need to understand the priorities of different issues.

Many developers have recently asked this question (link-1, link-2, link-3). In other words, whenever you develop a new extension, you need to design an icon, then you may encounter this problem. Supporting light and dark icons is very fundamental, it should get higher priority.

There is currently no ideal workaround. Except toolbar action icon, developers cannot control icons in other places. Even with the icons on the toolbar, developers can't listen to theme changes very well.

According to my observation, developers use the following workaround to solve it:

  • Use opposite colors to fill the background and the icon (or the icon shape and the icon inner).
  • Use a color that's not too dark and not too light for the icon.

This method is not the best effect, nor can it achieve effects like browser built-in icons. But this is the most common way to workaround, so the entire extension store is flooded with icons of this design.

@oliverdunk
Copy link
Member

We (Chrome) are currently working on an implementation of the icons.dark / icons.light and action.default_icon.dark / action.default_icon.light keys. We've realised there are a few areas of ambiguity in the proposal:

  • What happens if you specify the dark key but not light?
  • What happens if you specify dark, light and top-level size keys?
    • Is this an error or is it ok?
    • What if you are in light mode?
    • What if you are in a mode other than dark or light, which is possible in some cases?

My current thinking is:

  • We should support specifying all three simultaneously for backwards-compatibility reasons.
  • Once all browsers support this, our end goal should be that developers specify either light/dark or top-level sizes which would basically be a shorthand for setting dark and light to the same values.
  • We should fallback to the light icons for any theme that isn't dark.
  • If one of light or dark is missing, we first fallback to top-level size keys, and then fallback to just rendering the browser's default icon (a generated letter icon in Chrome).

@hanguokai
Copy link
Member Author

Here is my opinion.

What happens if you specify the dark key but not light?

The browser should give a warning, and use it for both dark and light. To use the dark/light keys, developers should provide two sets of icons, which is developers' obligation.

What happens if you specify dark, light and top-level size keys?

The dark/light keys have higher priority than top-level size keys, so the browser should ignore top-level size keys.

for backwards-compatibility

If old versions of browser can ignore the unknown dark/light keys, developers could provides both dark/light and top-level size keys. Otherwise, developers should only provide one of them, and set "minimum_chrome_version" for the dark/light keys.

We should fallback to the light icons for any theme that isn't dark.
What if you are in a mode other than dark or light, which is possible in some cases?

I have no clear idea. Can the browser automatically determine that the current theme is more inclined to the light or dark?

@carlosjeurissen
Copy link
Contributor

As mentioned during our 2023-12-07 meeting. An extension with icons.dark or action.default_icon.dark is not allowed to load in Google Chrome tested in Version 119.0.6045.123 (Official Build) (arm64) (MacOS).

Something to take into consideration is the difference between OS dark mode and the need for an icon which works on a dark or light background. The browser could have a custom theming which would mean the icon needed does not equivalent the OS Dark Mode. This is I believe why the theme_icons implementation mentions dark and light in reverse. Thus for backwards compatibility I still believe theme_icons could work well.

@hanguokai
Copy link
Member Author

At present, this issue is mainly about developers' feedback on functional requirements of extension icons on Dark/Light theme, rather than detailed proposal. And all browsers are supportive of this feature.

So this issue has achieved its goal. Next, I suggest that browsers submit a formal PR as the specification (like other browsers' proposals) to w3c/webextensions/proposals and discuss the specification details there. @oliverdunk @Rob--W @xeenon

@erosman
Copy link

erosman commented Dec 9, 2023

While considering a dark/light icon setting in manifest.json, it would be beneficial to include other associated aspects of toolbar icon i.e.

  • badge background color
  • badge text color

Just an idea...

{
  "action": {

    "theme": {

      "light": {
        "icons": {
          "16": "image/icon.svg",
          "32": "image/icon.svg"  
        },
        "badge_background_color": "#ffffff",
        "badge_text_color": "#000000"
      },
      
      "dark": {
        "icons": {
          "16": "image/icon-dark.svg",
          "32": "image/icon-dark.svg"
        },
        "badge_background_color": "#000000",
        "badge_text_color": "#ffffff"
      }
    }
  }
}

@oliverdunk
Copy link
Member

Agreed on the noise in this issue @hanguokai - continuing here for now but agree it might be good to have a new issue once we have a concrete proposal.

One thing that came up in the last meeting was that unfortunately icons.dark etc. fail to load in current versions of Chrome. This means to adopt those keys extensions would need to bump their minimum_chrome_version.

We haven't settled on anything yet, but with that in mind we've been discussing a few different options. One that has come up is a new icon_variants key inspired by (but different to) theme_icons:

"icon_variants": {
  "dark": {
    "64": "64_icon_dark.png"
  },
  "light": {
    "64": "64_icon_light.png"
  }
},
"action": {
  "default_icon": {
    "64": "images/64_icon.png"
  },
  "icon_variants": {
    "dark": {
      "64": "64_action_dark.png"
    },
    "light": {
      "64": "64_action_light.png"
    }
  }
}

Does this seem like a reasonable way to have a cross-browser key which is backwards compatible?

There would be a few things to decide about precedence but I think we can figure those out if this seems agreeable.

@JamesCoyle
Copy link

@oliverdunk that is exactly the API I was expecting to come from this issue. Assuming older browser versions can simply ignore the icon variants without it throwing an error I think this would be the best option.

In terms of precedence I'd suggest the following:

  1. Any programmatically defined icon
  2. Light/dark variants if specified depending on browser theme
  3. Default if any of the above unavailable or a custom browser theme is set

I would like to see a way to determine which of the icons was selected in JavaScript so that programmatically defined icons can also follow the theme setting.

It would perhaps be prudent to also add a high contrast option to the icon variants for improved accessibility.

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented Jan 4, 2024

@oliverdunk one suggestion is to work together with the Web Applications WG on how to best specify this in the manifest. They have some interesting discussions about this:
w3c/manifest#1045
w3c/manifest#975

In general it is good not to forget the need for icons which work well on a dark / light background independent of the operating system dark mode due to custom browser theme requirements.

My suggestion is to prioritise a way to set action icons for dark backgrounds programmatically.

@JamesCoyle Instead of allowing the extension to detect which mode is used (dark/light), I would propose setting both when calling action.setIcon to prevent a bunch of extensions needing to do work when the light/dark mode or theme of a browser changes. Think of something like:

browser.action.setIcon({
  imageData: {
    16: image16,
    32: image32,
  },
  darkBackground: {
      imageData: {
        16: image16,
        32: image32,
      }
  }
});

Either using dark or darkBackground is up for discussion. Using darkBackground here to emphasize the above mentioned need for icons which work well on dark backgrounds when in light mode because of custom browser themes.

Since the action button is the main area which has this need. We could allow changing badgeBackgroundColor and badgeTextColor in the calls as well. Think of something like this:

browser.action.setThemeData({
  "dark": {
    "badgeBackgroundColor": "lightgreen",
    "badgeTextColor": "black",
    "icon": "dark.svg",
  },
  "light": {
    "badgeBackgroundColor": "green",
    "badgeTextColor": "white",
    "icon": "light.svg",
  }
});

@JamesCoyle
Copy link

@carlosjeurissen extending setIcon to allow for the icon variants would work for me. I can't currently think of a use-case that wouldn't work with that.

@phaux
Copy link

phaux commented May 12, 2024

It would be nice if SVG icons simply supported fill="currentColor" or something similar.

Edit: this currently works thanks to fill="context-fill" special property, but it only works in official Mozilla extensions, because it's non-standard. The current Mozilla plan is to implement and use CSS linked parameters spec for that.

Edit2: w3c/csswg-drafts#9872

@xeenon
Copy link
Collaborator

xeenon commented May 20, 2024

@phaux I agree, that should work in Safari.

@carlosjeurissen
Copy link
Contributor

@phaux how about using an env variable for this to provide authors a way to have a fallback in case the browser does not support this feature?

@DanielHerr
Copy link

For my use case, I am dynamically creating the browser icon at runtime. In order to accommodate dark and light modes, I draw different styles to a canvas and then set the icon to the canvas contents. An API design which takes as input both images would be better than needing to detect the theme in a offscreen document, but would still require wasted work painting the unused theme's respective canvas.

Preferably media queries would be exposed to service workers, but something like browser.runtime.theme == "light" || "dark" would also be nice.

Also, notifications are another surface to consider.

@oliverdunk
Copy link
Member

An API design which takes as input both images would be better than needing to detect the theme in a offscreen document, but would still require wasted work painting the unused theme's respective canvas.

The tradeoff here is that it means the icons wouldn't be available when the theme first changes, and we couldn't update them at the same time as everything else. I don't think exposing media queries to service workers is off the table but I think we'd be hesitant to encourage doing reactive work based on theme changes, at least in the most common cases :)

Also, notifications are another surface to consider.

For sure!

@carlosjeurissen
Copy link
Contributor

carlosjeurissen commented May 28, 2024

@oliverdunk Very much agree with having both dark and light icons available so a theme switch can be made is a good one. However I believe exposing additional metadata for the action icon would be very useful in reducing the icons needed to generate and action icons are as pixel perfect as possible. For example, devicePixelRatio is currently not available in serviceWorkers sadly.

Think about a method like chrome.action.getIconSpecs() returning

{
    "size": 16,
    "dpi": 2,
    "actualSize": 32,
    "color_schemes": ["light", "dark"]
}

color_schemes would give the color schemes the browser supports. This again reduces the amount of icons needing to be generated with imageData.

@DanielHerr
Copy link

On the topic of DPR, I just realized that the multiple monitor use case is also something to consider. There can be multiple screens with different icon sizes.

So for a hypothetical function to get the icon size, you would need to return a set or array of icon sizes. You should then be able to use that with the existing size based action.setIcon, though I haven't tested whether that API handles multiple monitors correctly in browser implementations.

As far as I can tell calculating icon sizes for multiple screens is currently not trivally implemented.
On Chrome OS you can calculate the icon size as 16 * DPR as retrieved from chrome.system.display, which is easy enough.
However, that API doesn't expose the needed data on other platforms, so you have to fall back to either getScreenDetails on Chromium on Linux/Windows/Mac, which requires a permission prompt, not from the background but from an extension popup or tab.
Alternatively on Firefox creating a tab or popup on each display and retrieving DPR data from there, if the browser.windows.create API even allows that, or requiring a global content script injected just to read DPR on other screens.

Should this icon size retrieval topic be filed as a separate issue?

@xeenon
Copy link
Collaborator

xeenon commented Jun 4, 2024

A new issue would be best.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement or change to an existing feature supportive: chrome Supportive from Chrome supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari
Projects
None yet
Development

No branches or pull requests