- From: Daniel Murphy <notifications@github.com>
- Date: Fri, 06 Feb 2026 09:07:13 -0800
- To: w3c/manifest <manifest@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <w3c/manifest/issues/975/3861597966@github.com>
dmurph left a comment (w3c/manifest#975)
Reflecting after manifest meeting with @marcoscaceres yesterday:
- The most important things to do dark mode / light mode here are theme color and background color.
- Icon localization + dark mode / light mode seems probably rare, and I would actually say that it would require some UX changes in Chrome for us to allow to both a light mode and a dark mode icon (as we would need to present them to the user, both). However, I could easily see this just happening one time when the dark mode / light mode preferences changes, where we present it like an update but with better language
- e.g. "This app has support for a dark/light mode icon. Please approve the icon change for dark/light mode."
- <approve / ignore / uninstall / learn more>
So - I think my proposal is this, which IMO solves this nicely in a way that seems readable and allows us to hopefully unify with WebExtensions. It adds the 'type' and other attributes we were using before, but has the new per-size grouping.
- It adds WxH parsing for icon size
- It formalizes the choosing algorithm, and makes a static constraint fallback order (happy to hear feedback on this)
"icons":
```json
{
"32": "32-size-fr.png",
"64": "64-size-fr.png",
"type": "image/png",
"lang": "fr",
"purpose": "any"
},
{
"32": "32-size-fr-mask.png",
"64": "64-size-fr-mask.png",
"type": "image/png",
"lang": "fr",
"purpose": "maskable"
},
{
"32": "32-size.png",
"64": "64-size.png",
"type": "image/png",
"purpose": "any"
},
{
"32": "32-size-dark.png",
"64": "64-size-dark.png",
"type": "image/png",
"color-scheme": "dark",
"purpose": "any"
},
{
"32": "32-size-dark-maskable.png",
"64": "64-size-dark-maskable.png",
"type": "image/png",
"color-scheme": "dark",
"purpose": "maskable"
},
```
"screenshots":
```json
{
"1280x720": "screenshots/home.webp",
"type": "image/webp",
"form_factor": "wide",
"label": "Home screen showing main navigation and featured content"
},
{
"1280x720": "screenshots/dashboard.webp",
"type": "image/webp",
"platform": "ios",
"label": "Dashboard view displaying key metrics"
}
```
Algorithm for a user agent to choose an icon group:
- User agent then specifies:
- the following 3 constraints, ordered by importance:
1. Color Scheme <dark, light>
3. Purpose <any, maskable, monochrome, unspecified>
4. Language
- User agent first removes all icon groups / entries that are fully not supported
- e.g. on desktop we would remove all 'monochrome' groups, as that is not supported, only on Android. Windows doesn't do masking yet, so it would also remove 'maskable'.
- same with 'platform' for screenshots.
- user agents can also probably have a maximum size too - e.g. nothing larger than 2096
- probably remove entries with no icon paths too (or those just don't parse)
- `found_icon_group = null`
- Loop through all icon entries:
- If the entry satisfies all constraints, set `found_icon_group` to the group
- If `found_icon_group == null`
- exit if no constrains left with `null`
- (This might happen if user agent doesn't require a basic 'any' icon for installation - usually they seem to choose the favicon for this, so for those user agents, the saved favicon can be the fallback)
- remove the last constraint from the list, and GOTO the loop again 😛
This matches our current implementation for choosing icons for a platform given a purpose
Then, algorithm for user agent to choose an icon from a group:
- User agent specifies an ideal size, WxH
- `any_size_icon_path = null`
- `closest_larger_icon = null`
- `closest_smaller_icon = null`
- loop through sizes in the icon group
- if the size matches the ideal size, return that.
- if the size is larger, set it to `closest_larger_icon` (do standard distance stuff if there was one already found)
- if the size is smaller, set it to `closest_smaller_icon` (do standard distance stuff if there was one already found)
- if the size matches `any`, set `any_size_icon_path` to the icon path
- if `any_size_icon_path != null`, then return `any_size_icon_path`
- if `closest_larger_icon != null`, then return `closest_larger_icon`
- if `closest_smaller_icon != null`, then return `closest_smaller_icon`
- return `null`
Using the above algorithms, examples of queries for groups with
- light, maskable, french -> `*-size-fr-mask.png` - found all!
- light, maskable, english -> `*-size.png` (not maskable, preferred light)
- dark, maskable, french -> `*-size-dark.png` (not french or maskable, preferred dark)
The existing constraint fallback is what seems correct to me, where the light/dark is the most jarring / should get right, then any vs maskable for platform support, then language. But I'm happy to have that order changed, or... I guess say the user agent can specify. But I think that it's better to have this be extra predictable for devs. We can even make an easy demo site for people to understand, given a manifest, what is chosen when.
--
Reply to this email directly or view it on GitHub:
https://github.com/w3c/manifest/issues/975#issuecomment-3861597966
You are receiving this because you are subscribed to this thread.
Message ID: <w3c/manifest/issues/975/3861597966@github.com>
Received on Friday, 6 February 2026 17:07:17 UTC