[w3c/manifest] Web Manifest Overrides (Issue #1045)

We’ve known for a while that we need some way to override the value of manifest members based on some “environmental  condition”, be it user’s preferred language, OS color scheme (i.e., light or dark mode), or something we haven’t thought of yet.

The working assumption is that the overrides may apply at install time and may persist after installation. For example, if an installed application is running, and the OS switches color scheme to “dark mode”, the user agent should apply a matching theme_color for the dark color scheme.

Thus, the solution needs to cater for both OS level changes independently of the browser engine running, and the browser engine should be able to re-apply a manifest to reflect the runtime environmental conditions at any moment in time.  

## Requirements 

The solution needs to be:

* **Built for purpose**: excluding hypothetical use cases. 
* **Backwards compatible**: must work on existing user agents. 
* **Easy to use and process**: it should avoid, for instance, excessive nesting, and not dependent on array order from other members in the manifest.
* **Designed to handle user preferences and language selection** (preferably all at once).

## Related issues

The following issues and pull request have driven this discussion: 

* https://github.com/w3c/manifest/issues/975 (theme color)
* https://github.com/w3c/manifest/issues/676 (translations)
* https://github.com/w3c/image-resource/issues/26 (ImageResource color_scheme)

## Points of contention

Throughout the discussion, a few points of contention have arisen. There are valid arguments made for an against each of the following. 

### CSS media queries VS using manifest members

A primary point of contention is the use of CSS Media Queries VS using manifest members that represent the same thing. On the one hand, media queries define a lot of what we want (i.e., all the “prefers-“ media features and values). On the other hand, evaluation of those media features requires a CSS runtime. 

As stated in #975: 

> “the challenge we face is then who actually evaluates the `<media-query-list>` to do the member selection? If it's the OS, then "light" and "dark" is definitely better: we can't expect an OS to do anything worthwhile with `<media-query-list>`, and converting `<media-query-list>` to something that the OS understands would be quite challenging and error prone.

Although the above holds true as a general statement, in the case of icons, the above currently has no bearing because operating systems don’t support switching application icons when the theme changes. 

Further, some OSs restrain what iconography can be shown in shortcuts, again limiting the possibility of developers having to provide icons to the OS to deal with OS-level user preferences.  Hence, if overrides only apply to members that are in control of the user agent, and which are applied at runtime: where interpreting a CSS media query is possible. 

The point being: some members (e.g., `theme_color`) are interpreted at runtime by the browser engine, while others might need to be handled specifically by the OS. We should handle those individually and on a case-by-case basis.

### Parsable property keys

Another point of contention has been using “parsable keys”, where by a key contains either a CSS media query or a language-tag. For example:

```JSON
{
   "(prefers-color-scheme: dark)": {...},
   "(prefers-color-scheme: light)": {},  
}
```

As opposed to using named keys with objects inside an array:

```JSON
{
  "overrides": [
     {"media": "(prefers-color-scheme: dark)"},
     {"media": "(prefers-color-scheme: light)"},   
  ]
}
```

Using parsable keys is fairly common in JSON. However, where the order of keys is important, using parsable keys relies on JavaScript-based manifest parser in order to work: this is because JS treats JSON’s keys as an ordered map. As seen above, it also results in a flatter structure when compared to using arrays and objects. 

However, a significant drawback is that a parsable-key structure **is not extensible**: The key needs to have all the information in it to support every type of matching. This works ok for matching user-preferences via media queries, but might not work for languages (because languages are not currently a media feature that can be matched). 

On the other hand, using and array and a set of objects overcomes these issues at the cost of adding some verbosity. Nevertheless, using arrays + objects has several advantages:

* doesn't depend on member order, but instead relies on array order, which overcomes the JS-JSON quirk where keys are an ordered map. 
* is extensible. So, for instance a “language” and “dir” members could be added to members that need it. Or, specific “media” queries or other user-preference determining members can be added as needed for a particular purpose.

### Issue with translations proposal

Note that the “translations” proposal suffers from the same issues listed above, because it relies on a (parsable) language-tag as a key. 

```JSON
{
   "translations":
      "en": {
        "short_name": "whatever"
      }
}
```

Language selection is generally done using Unicode’s “lookup” algorithm, meaning that it’s not a simple match. The solution may also be  inadequate in that it doesn’t allow specifying per member direction and language override. If individual direction and language is a requirement, then the following object structure is more appropriate:


```JSON
{ "text": "some text", dir: "ltr|rtl|auto", lang: "language-tag" }
```

## Media preference overrides

Another proposed solution was to have a grouping object with a special key (in this case `prefers_color_scheme`). 

```JSON
{
    "user_preferences": [
        {
            "prefers_color_scheme": "light",
            "theme_color": "white",
            "background_color": "black"
        },
        {
            "prefers_color_scheme": "dark",
            "theme_color": "black",
            "background_color": "white"
        }
    ]
}
```

The problem is that it’s overly broad in its applicability. For example, the following doesn’t make sense:

```JSON
{
    "prefers_color_scheme": "light",
    "name": "light",
    "lang": "jp"
},
{
    "prefers_color_scheme": "dark",
    "name": "dark",
    "lang": "jp"
}
```

That is to say, changing the color scheme shouldn’t (and won’t) have any effect on the name of the application. The problem is that it’s not immediately obvious as to what `prefers_color_scheme` applies. 

## The redefinitions proposal

Another proposal is to use specific members, which include an array of objects that provide a “context” and an “override”. 

```JSON
 "user_preferences": [
    {
      "media": "(prefers-color-scheme: dark)",
      "override": {
        "theme_color": "#bdbdbd",
        "background_color": "#000000"
      }
    },
  ],
  "translations": [
    {
      "language": "de",
      "redefine": {
        "name": "Meine super coole App"
      }
    }
  ]
```

Although compact, this solution suffers from the same issues already mentioned above: in that a developer could end up adding members that are unaffected by the “context”. For example, adding “theme_color” under “translations“. 

Thomas Steiner also showed that this separation between “translations” and “user_preferences” becomes unmanageable:
https://github.com/w3c/manifest/issues/975#issuecomment-867512145


## Purpose-built objects and members

Given that we need to override existing manifest members, another proposal is to just introduce new members and purpose-built objects.  Then, each could contain an array of objects with a condition for the override it. 

Thus, we could define a purpose-built “color object” and pluralize the name of “theme_color”. For example:

```JSON
`"theme_color": "red",
"theme_colors": [
    { "color": "red" },
    { "color": "darkred", "media": "(prefers-color-scheme: dark)" }
]`
```

A potential problem with pluralization of member names is that we already have some members whose names are in plural form (icons, shortcuts), so this wouldn’t be backwards compatible. So, might need a new member for such cases (or we take the compat hit).

A possible solution might looks like:

```JSON
{
    "shortcuts": [
        {
            "name": "Play Later",
            "localized_names": [{ "value": "Jugar Mas Tarde", "lang": "es" }],
            "description": "View the list of podcasts you saved for later",
            "localized_descriptions": [
                {
                    "value": "Ver la lista de podcasts que guardó para más tarde",
                    "lang": "es",
                    "dir": "ltr"
                }
            ],
            "url": "/play-later",
            "icons": [
                { "src": "/icons/play-later.svg", "type": "image/svg+xml" }
            ],
            "other_icons": [
                { "src": "/icons/play-later.svg", "scheme": "dark" },
                { "src": "/icons/en/play-later.svg", "scheme": "dark", "lang": "en" }
            ]
        }
    ]
}
```

 
It would be simple enough to apply the same principle to the “name“, ”short_name“ members, as well as the ”description“ member by defining a ”localizable object“ structure. 

```JSON
"short_name": "dog",
"localized_short_names": [
  {"value": "perro", lang: "es", dir: "ltr"},
  {"value": "כֶּלֶב", lang: "he", dir: "rtl"}
]
```

Despite having include new members, it does meet all the requirements:

* **✅ Built for purpose**: each member type can only accept a specific kind of object (e..g, localizable object, color object, etc.). This is already how the manifest format works.  
* **✅ Backwards compatible**: if we add new members we don’t break backwards compatibility. 
* **✅ Easy to use**: there is no excessive nesting and everything is kept together for each object type. 
* **✅ Designed to handle user preferences and language selection:** because the objects and members are built for purpose, they cary all their own selection criteria (be it at media query, language, or whatever), which solves for matching selection being overly broad. 

## Conclusions

Although we have a number of different solutions, none are without their own set of problems. However, of all the approaches, the “purpose-built objects and members” seems to be the least problematic and one that best meets the requirements. 


-- 
Reply to this email directly or view it on GitHub:
https://github.com/w3c/manifest/issues/1045

You are receiving this because you are subscribed to this thread.

Message ID: <w3c/manifest/issues/1045@github.com>

Received on Friday, 15 July 2022 07:52:40 UTC