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

Adding User Preferences #57

Draft
wants to merge 9 commits into
base: gh-pages
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 282 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,288 @@ <h2>
</p>
</section>
</section>
<section>
<h2>
<code><dfn>user_preferences</dfn></code> member
</h2>
<p>
The `user_preferences` member of the [=application manifest=] is an
object that can be used to override values of manfiest members depending
on which user preferences are set. It has the following members:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For:

"on which user preferences are set"

We should check the language that CSS uses for this, as it's not clear what "user preferences" means.

</p>
<ul>
<li>[=user_preferences/color_scheme=]
Copy link
Collaborator Author

@aarongustafson aarongustafson Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@loubrett What do you think about adding contrast to this as well? If not, we should add a note that we expect future user preferences to be supported (e.g., contrast, reduced motion, etc.).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added it as a note for now - happy to instead add it to the list if you prefer. I guess it'll need its own section if I add it to the list. Would we just want less and more for contrast?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dig the note.

</li>
</ul>
<p class="note">
This list of members is expected to expand in the future to include
other <a data-cite="mediaqueries-5/#mf-user-preferences">user
Copy link
Contributor

@marcoscaceres marcoscaceres Jun 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid hard links (data-cite="x#y") to other specs... better to file a bug on the CSS spec to export the terms (bonus points for sending a PR to export the terms needed!🏆)

Also, please avoid link to complete sections in the CSS spec. If we need to share concepts, we should try to specify that.

preference media features</a> such as <a data-cite=
"mediaqueries-5/#prefers-contrast">contrast</a> and
<a data-cite="mediaqueries-5/#forced-colors">forced-colors</a>,
as defined in CSS.
</p>
<p>
The user agent SHOULD use the override values instead of the value of
the corresponding member defined at the top level of the manifest.
</p>
<p>
To <dfn>process the `user_preferences` member</dfn>, given
[=ordered map=] |json:ordered_map|, [=ordered map=] |manifest:ordered map|,
run the following during the [=application manifest/processing
extension-point=] in [=processing a manifest=]:
</p>
<ol class="algorithm">
<li>If |json|["user_preferences"] does not [=map/exist=], return.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should happen in the main processing steps.

</li>
<li>If the type of |json|["user_preferences"] is not [=ordered map=],
return.
</li>
<li>Set |manifest|["user_preferences"] to a new [=ordered map=].
</li>
<li>[=Process the `color_scheme` member=] passing
|json|["user_preferences"] and |manifest|["user_preferences"].
</li>
</ol>
<section>
<h3>
<code><dfn data-dfn-for="user_preferences">color_scheme</dfn></code>
member
</h3>
<p>
The [=user_preferences=] `color_scheme` member is an object that
contains the overrides for color scheme preferences. It has the
following members:
</p>
<p>
The <code><dfn data-dfn-for="color_scheme">dark</dfn></code> member
specifies the manifest overrides to use when the user prefers a
dark theme.
</p>
<p>
The <code><dfn data-dfn-for="color_scheme">light</dfn></code> member
specifies the manifest overrides to use when the user prefers a
light theme.
</p>
<p>
To <dfn>process the `color_scheme` member</dfn>, given [=ordered map=]
|json_user_preferences:ordered_map|, [=ordered map=]
|manifest_user_preferences:ordered map|, run the following:
</p>
<ol class="algorithm">
<li>If |json_user_preferences|["color_scheme"] does not [=map/exist=],
return.
</li>
<li>If the type of |json_user_preferences|["color_scheme"] is not
[=ordered map=], return.
</li>
<li>Set |manifest_user_preferences|["color_scheme"] to a new
[=ordered map=].
</li>
<li>[=list/For each=] |member:string| of [=list=] « "dark", "light" »:
<ol>
<li>Let |overrides:ordered map| be the result of processing a
manifest override object.
</li>
<li>Set |manifest_user_preferences|["color_scheme"][|member:string|]
to |overrides|.
</li>
</ol>
</li>
</ol>
</section>
<section>
<h3>
Overridable properties
</h3>
<p>
The proterties that [=user_preferences=] can override in the
[=manifest override object=] are:
</p>
<ul>
<li>[=manifest/theme_color=] member.
</li>
<li>[=manifest/background_color=] member.
</li>
<li><a data-cite="appmanifest/#shortcut-items">Shortcut item's</a>
<a data-cite="appmanifest#icons-member-0">icons</a>
<a data-cite="image-resource/#dfn-src">src</a> member.
</li>
<li><a data-cite="appmanifest/#shortcut-items">Shortcut item's</a>
<a data-cite="appmanifest#icons-member-0">icons</a>
<a data-cite="image-resource/#dfn-type">type</a> member.
</li>
<li>[=manifest/icons=] <a data-cite=
"image-resource/#dfn-src">src</a> member.
</li>
<li>[=manifest/icons=] <a data-cite=
"image-resource/#dfn-type">type</a> member.
</li>
</ul>
</section>
</section>
<section data-cite="image-resource">
<h2>
Manifest override objects
</h2>
<p>
Each <dfn>manifest override object</dfn> is a generic object value
that allows for certain manifest properties to be overridden within
a particular context.
</p>
<p>The structure of a [=manifest override object=] is as follows:</p>
<pre class="json">
{
"context_key": {
"property": "new value"
}
}
</pre>
<p>
Each manifest property that accepts a [=manifest override object=]
as its value will define the contexts it supports and which
properties it supports overriding. User agents MUST ignore any contexts
that are not supported by the property as well as any override
properties not explicitly allowed within it.
</p>
<p>
When the manifest property’s context is applicable, the value of each
allowable override will be used in place of the original value defined
in the Manifest.
</p>
<p>
Redefined array items will be overridden in the order they are authored.
When redefining objects (e.g., [=manifest/shortcuts=], [=manifest/icons=]),
authors will only be able to redefine specific properties of that object.
In order to ensure all overrides are applied correctly, the order must
match the original array (i.e., each [=manifest/shortcut=] must be redefined
in order, as must their icons, if they also require re-definition).
</p>
Redefined array items must also be equal in number to the array being
overridden. If there is a mismatch in the number of items in either array,
any excess items will be ignored. This is only an issue if the original
array has more items than the override array, because any excess items
within the original array will not be re-defined.
</p>
<p>
When there is a conflict because two different properties are attempting
to override the same value in their respective active contexts, the one
defined last will win. By way of example, consider the following:
</p>
<pre class="json">
{
"lang": "en-US",
"icons": [
{
"src": "icon.png",
"sizes": "128x128",
"type": "image/png"
}
],
"translations": {
"es": {
"icons": [
{
"src": "icon-es.png"
}
]
}
},
"user_preferences": {
"color_scheme": {
"dark": {
"icons": [
{
"src": "icon-dark.png"
}
]
}
}
}
}
</pre>
<p>
In this example, if the user’s primary language is Español, but
their preferred color scheme was set to "dark", the icon supplied
would be the dark version and not the localized one. For this reason,
it is imperative that properties taking a [=manifest override object=]
as their value consider whether any other properties that also enable
overrides should be able to be redefined within them. In the above
example, the author’s intent would likely have been better realized
if `user_preferences` was put before `translations` and the localized
context block redefined the `user_preferences` value for that language:
</p>
<pre class="json">
{
"lang": "en-US",
"icons": [
{
"src": "icon.png",
"sizes": "128x128",
"type": "image/png"
}
],
"user_preferences": {
"color_scheme": {
"dark": {
"icons": [
{
"src": "icon-dark.png"
}
]
}
}
},
"translations": {
"es": {
"icons": [
{
"src": "icon-es.png"
}
],
"user_preferences": {
"color_scheme": {
"dark": {
"icons": [
{
"src": "icon-es-dark.png"
}
]
}
}
}
}
},

}
</pre>
<p>
User agents MAY ignore any override properties they do not support.
</p>
<p>
To <dfn>apply a manifest override object</dfn>, given [=ordered_map=]
|overrides:json|, [=ordered map=]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|overrides:json|, [=ordered map=]
|overrides:ordered map|, [=ordered map=]

|manifest:ordered map|, and [=array=] |allowed_properties:array| run the following:
</p>
<ol class="algorithm">
<li>Let |allowed_overrides| be a new [=ordered map=].
</li>
<li>[=list/For each=] |member:string| of |overrides|:
<ol>
<li>If |member| is not in |allowed_properties|, continue.
</li>
<li>Set |allowed_overrides|[|member|] to |overrides[|member|]|.
</li>
</ol>
</li>
<li>Let |overriden_manifest| be the result of creating a new [=ordered map=]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcoscaceres I’m not sure the best way to express this, but in JavaScript it would be merging the objects using the spread operator:

let overriden_manifest = {
  …manifest,
  …allowed_overrides
};

Copy link
Contributor

@marcoscaceres marcoscaceres Jun 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See map/iterate ... something like:

[=map/For each=] |key| → |value| of |overrides|, set |manifest|[key] to |value|.

Should do it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think that handles nested objects as neatly as spread, but I should be able to recursively call it if the value is an object.

by merging |manifest| with |allowed_overrides|.
</li>
<li>
Set |manifest| to |overriden_manifest|.
</li>
</ol>
</section>
<section data-cite="DOM">
<h2>
Installation prompts
Expand Down