├── .github └── workflows │ └── build.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY-AND-PRIVACY-SELF-REVIEW.md ├── index.html └── w3c.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | name: Build 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Assemble out/ directory 16 | run: | 17 | mkdir out 18 | mv index.html out/index.html 19 | - name: Deploy 20 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./out -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,windows,linux,intellij+all,visualstudiocode 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,linux,intellij+all,visualstudiocode 3 | 4 | ### Intellij+all ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # AWS User-specific 16 | .idea/**/aws.xml 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | # .idea/artifacts 39 | # .idea/compiler.xml 40 | # .idea/jarRepositories.xml 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # SonarLint plugin 69 | .idea/sonarlint/ 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | 80 | # Android studio 3.1+ serialized cache file 81 | .idea/caches/build_file_checksums.ser 82 | 83 | ### Intellij+all Patch ### 84 | # Ignore everything but code style settings and run configurations 85 | # that are supposed to be shared within teams. 86 | 87 | .idea/* 88 | 89 | !.idea/codeStyles 90 | !.idea/runConfigurations 91 | 92 | ### Linux ### 93 | *~ 94 | 95 | # temporary files which can be created if a process still has a handle open of a deleted file 96 | .fuse_hidden* 97 | 98 | # KDE directory preferences 99 | .directory 100 | 101 | # Linux trash folder which might appear on any partition or disk 102 | .Trash-* 103 | 104 | # .nfs files are created when an open file is removed but is still being accessed 105 | .nfs* 106 | 107 | ### macOS ### 108 | # General 109 | .DS_Store 110 | .AppleDouble 111 | .LSOverride 112 | 113 | # Icon must end with two \r 114 | Icon 115 | 116 | 117 | # Thumbnails 118 | ._* 119 | 120 | # Files that might appear in the root of a volume 121 | .DocumentRevisions-V100 122 | .fseventsd 123 | .Spotlight-V100 124 | .TemporaryItems 125 | .Trashes 126 | .VolumeIcon.icns 127 | .com.apple.timemachine.donotpresent 128 | 129 | # Directories potentially created on remote AFP share 130 | .AppleDB 131 | .AppleDesktop 132 | Network Trash Folder 133 | Temporary Items 134 | .apdisk 135 | 136 | ### macOS Patch ### 137 | # iCloud generated files 138 | *.icloud 139 | 140 | ### VisualStudioCode ### 141 | .vscode/* 142 | !.vscode/settings.json 143 | !.vscode/tasks.json 144 | !.vscode/launch.json 145 | !.vscode/extensions.json 146 | !.vscode/*.code-snippets 147 | 148 | # Local History for Visual Studio Code 149 | .history/ 150 | 151 | # Built Visual Studio Code Extensions 152 | *.vsix 153 | 154 | ### VisualStudioCode Patch ### 155 | # Ignore all local history of files 156 | .history 157 | .ionide 158 | 159 | ### Windows ### 160 | # Windows thumbnail cache files 161 | Thumbs.db 162 | Thumbs.db:encryptable 163 | ehthumbs.db 164 | ehthumbs_vista.db 165 | 166 | # Dump file 167 | *.stackdump 168 | 169 | # Folder config file 170 | [Dd]esktop.ini 171 | 172 | # Recycle Bin used on file shares 173 | $RECYCLE.BIN/ 174 | 175 | # Windows Installer files 176 | *.cab 177 | *.msi 178 | *.msix 179 | *.msm 180 | *.msp 181 | 182 | # Windows shortcuts 183 | *.lnk 184 | 185 | # End of https://www.toptal.com/developers/gitignore/api/macos,windows,linux,intellij+all,visualstudiocode 186 | 187 | .fleet/* 188 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Preferences API 2 | 3 | This repository is being used for work in the W3C Web Preferences API, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors under the 2 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 3 | Contributions to Specifications are made under the [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Preferences API 2 | 3 | Authors: 4 | [Luke Warlow](https://github.com/lukewarlow) 5 | 6 | Contributors: 7 | [Jason Williams](https://github.com/jasonwilliams) (Bloomberg) 8 | 9 | ## Status of this Document 10 | 11 | Status: Retired moved to https://drafts.csswg.org/mediaqueries-5/#auto-pref%E2%91%A0 12 | 13 | Continue discussion in CSSWG issue repo. 14 | 15 | Below is the original explainer it may be outdated. 16 | 17 | ------------------- 18 | 19 | This document is intended as a starting point to engage standards bodies in developing a solution to the problem listed below. 20 | 21 | See [the draft spec](https://wicg.github.io/web-preferences-api/) for more details. 22 | 23 | ## Introduction 24 | 25 | Currently, website authors have a choice when wishing to honour a user's preference for a given setting: 26 | 27 | They can choose to "use the platform" where the user must indicate their preference via their OS or, if lucky, they can override in the browser. This comes with a number of issues: 28 | 29 | - Relies on the user's OS or browser offering the ability to change the setting 30 | - Relies on the user knowing how to change the setting in their OS or browser 31 | - No ability to override the setting for a specific site 32 | - No ability to sync preferences for a site across devices 33 | 34 | Alternatively, sites can and do offer site-level settings, but this currently comes with a number of issues: 35 | 36 | - No integration with CSS preference media queries 37 | - No integration with conditional resource loading (e.g. using ``) 38 | - No integration with JS APIs for retrieval of these preferences (e.g. `matchMedia`) 39 | - No integration with [User Preference Client Hints](https://web.dev/user-preference-media-features-headers/) 40 | - No integration with the `color-scheme` CSS property 41 | - The various client storage mechanisms that could store these preferences can be cleared in a number of scenarios 42 | 43 | The **Web Preferences API** aims to solve this by providing a way for sites to indicate a user preference for a given pre-defined setting. 44 | 45 | It is intended for this override to apply permanently and be scoped per origin. 46 | The override should be passed down to sub-resource where possible, see privacy section for details. This explainer refers to "site" but it should be read to mean origin. 47 | 48 | ### Goals 49 | 50 | - Provide a way for sites to override a given user preference in a way that fully integrates with existing browser APIs 51 | - Increase usage of these preferences, leading to a more accessible web 52 | 53 | ### Non-Goals 54 | 55 | - Provide a way for sites to store arbitrary site-specific preferences -- local storage or other storage APIs should be used instead 56 | - Provide a way for sites to determine the source of a user preference, beyond User Agent VS site (e.g. a site won't be able to determine if a setting comes from the OS or browser) 57 | - Force browsers to provide a UI for overriding OS level user preferences (although this would be nice) 58 | - Force browsers to provide a UI for overriding user preferences per site (although this would be nice) 59 | 60 | ## Demonstration 61 | 62 | You can try it out by running a recent version of Chrome Canary with the following flags enabled: `--enable-experimental-web-platform-features` 63 | 64 | ## Simple Example 65 | 66 | A common use case for this API would be allowing a user to override their color scheme preference for a given site. Usually via some kind of toggle switch. Here is an example of how this could be implemented: 67 | 68 | ```js 69 | button.onClick(() => { 70 | // Toggle the color scheme preference 71 | const newVal = 72 | navigator.preferences.colorScheme.value === "dark" ? "light" : "dark"; 73 | navigator.preferences.colorScheme 74 | .requestOverride(newVal) 75 | .then(() => { 76 | // The preference override was successful. 77 | }) 78 | .catch((error) => { 79 | // The preference override request was rejected. 80 | }); 81 | }); 82 | ``` 83 | 84 | ## Use Cases 85 | 86 | ### Color scheme toggle switch 87 | 88 | Currently, sites can use a variety of UI components to implement a per site configuration of color scheme preference. 89 | An example of a custom element library for this is [dark-mode-toggle](https://www.webcomponents.org/element/dark-mode-toggle). 90 | 91 | This library currently requires users to use a class to indicate the dark mode preference, rather than being able to use the preference media query. 92 | It also contains a "hack" to allow the media attribute on `` elements to work. 93 | 94 | With the **Web Preferences API**, this library could be updated to remove the "hack" for `` elements, and all limitations such as requiring a dark mode class would be removed. 95 | 96 | ### Syncing preferences across devices 97 | 98 | Like with the previous use case, if a site wanted to sync a user's animation preference across devices, they'd currently have to remove any usages of media queries and swap to using the DOM and a CSS selector (e.g. class, or attribute). 99 | 100 | With the **Web Preference API**, this would no longer be the case and sites could use a simple sync function on page load to ensure the server and client preference matches. 101 | 102 | This would have the added effect of the site benefiting from any potential future UA stylesheet to reduce animations for users who have indicated a preference for reduced motion. 103 | 104 | ### Fully Themed Browser UI 105 | 106 | Currently, if a site decides not to use `prefers-color-scheme`, they're likely also not using the `color-scheme` property to declare support for dark mode. 107 | 108 | This likely results in having to manually theme all browser provided UI (e.g. form controls, scrollbars) for dark mode. This is a lot of work and is likely to be missed in some places. 109 | 110 | With the **Web Preferences API**, sites could simply use the `color-scheme` property and rely on the browser to theme all browser provided UI. 111 | 112 | ## Proposed Solution 113 | 114 | ### The `navigator.preferences` object 115 | 116 | A new `navigator.preferences` object will be added to the platform. This object will be the entry point to this API. 117 | 118 | #### TypeScript 119 | 120 | ```ts 121 | interface Navigator { 122 | readonly preferences: PreferenceManager; 123 | } 124 | 125 | interface PreferenceManager { 126 | readonly colorScheme: PreferenceObject; 127 | readonly contrast: PreferenceObject; 128 | readonly reducedMotion: PreferenceObject; 129 | readonly reducedTransparency: PreferenceObject; 130 | readonly reducedData: PreferenceObject; 131 | // Future preferences can be added here, the exact properties will be down to the browser support. 132 | } 133 | 134 | interface PreferenceObject { 135 | // null means the preference is not overridden 136 | readonly override: string | null; 137 | readonly value: string; 138 | readonly validValues: string[]; 139 | 140 | requestOverride(value: string | null): Promise; 141 | clearOverride(): void; 142 | } 143 | 144 | interface PreferenceSupportData { 145 | readonly name: string; 146 | readonly values: string[]; 147 | } 148 | ``` 149 | 150 | ## Privacy and Security Considerations 151 | 152 | ### Avoiding fingerprinting 153 | 154 | This API exposes no new fingerprinting surfaces beyond that which already exist in the platform. 155 | 156 | ### Permissions & User Activation 157 | 158 | As the `requestOverride` method is a promise it gives user agents more control over the process of overriding a preference. 159 | 160 | The `requestOverride` method is gated behind a UA defined algorithm for determining if the action can proceed. 161 | 162 | This could include a user prompt, or it could be a simple check to see if the user has interacted with the page. 163 | 164 | ### Iframes etc 165 | 166 | See [#8](https://github.com/lukewarlow/web-preferences-api/issues/8) for discussion regarding this. 167 | 168 | For the spec we can probably find an existing definition to reference, but for the purposes of this explainer: 169 | 170 | - Any same-origin subresource (e.g. iframes) should get the overridden value. 171 | - Any cross-origin subresource that already has communication with the parent (e.g. `postMessage`) should get the override value. 172 | - Any cross-origin subresource with no external communication (e.g. an SVG loaded as an image) should get the override value. 173 | - Any cross-origin subresource that has no communication with parent but can communicate externally should **NOT** get the override value. 174 | 175 | Wherever the override value is passed down it should be done so in an opaque manner. 176 | 177 | e.g. if the parent frame sets `colorScheme` to `dark` then the iframe should see `prefers-color-scheme` as dark but shouldn't read `navigator.preferences.colorScheme` as `dark`. 178 | 179 | ## Alternative Solutions 180 | 181 | ### Use a custom media query 182 | 183 | A site could hypothetically use the as yet unimplemented [Custom Media Queries](https://drafts.csswg.org/mediaqueries-5/#script-custom-mq) 184 | this way they could define custom media queries for each preference they wish to override. 185 | 186 | This spec is currently in the early stages of development, and it is unclear if it will ever be implemented or in what shape it will take. 187 | For example, would this allow a site to use a custom media query inside the media attribute of a `` element? 188 | 189 | This also doesn't solve the key issue of third party libraries being aware of this preference override. 190 | 191 | ### Request that browsers provide a UI for overriding preferences per site 192 | 193 | While this would be a nice solution, the lack of any such UI in any browser makes it unlikely that this will happen any time soon. 194 | 195 | This also doesn't fix the (relatively minor) issue of preference syncing across devices. 196 | 197 | ## Open Questions 198 | 199 | - Where should the PreferenceManager interface be exposed? 200 | - It is currently exposed on the `navigator` object, is this best? 201 | - It is currently only exposed to Window, should it also be exposed to Service and/or Web Workers? 202 | 203 | ## Acknowledgements 204 | 205 | Special thanks to [Ryan Christian](https://github.com/rschristian) for his help in reviewing the original explainer and providing feedback. 206 | -------------------------------------------------------------------------------- /SECURITY-AND-PRIVACY-SELF-REVIEW.md: -------------------------------------------------------------------------------- 1 | # [Self-Review Questionnaire: Security and Privacy](https://w3ctag.github.io/security-questionnaire/) 2 | 3 | 4 | > 01. What information does this feature expose, and for what purposes? 5 | > 02. Do features in your specification expose the minimum amount of information 6 | > necessary to implement the intended functionality? 7 | 8 | This feature exposes no new information. 9 | 10 | > 03. Do the features in your specification expose personal information, 11 | > personally-identifiable information (PII), or information derived from 12 | > either? 13 | 14 | No. 15 | 16 | > 04. How do the features in your specification deal with sensitive information? 17 | 18 | This API does not interact with any sensitive information. 19 | 20 | > 05. Do the features in your specification introduce state 21 | > that persists across browsing sessions? 22 | 23 | Yes, the API is designed to persist user preference overrides across browsing sessions. 24 | 25 | > 06. Do the features in your specification expose information about the 26 | > underlying platform to origins? 27 | 28 | No. 29 | 30 | > 07. Does this specification allow an origin to send data to the underlying 31 | > platform? 32 | 33 | No. 34 | 35 | > 08. Do features in this specification enable access to device sensors? 36 | 37 | No. 38 | 39 | > 09. Do features in this specification enable new script execution/loading 40 | > mechanisms? 41 | 42 | No. 43 | 44 | > 10. Do features in this specification allow an origin to access other devices? 45 | 46 | No. 47 | 48 | > 11. Do features in this specification allow an origin some measure of control over 49 | > a user agent's native UI? 50 | 51 | Other than the ability to override a users color-scheme preference for the current origin which can impact: 52 | - scrollbars (this isn't a new capability though), 53 | - the rendered favicon 54 | - form controls rendering 55 | 56 | This API provides no control over native UI. Also all 3 of the above are already author controllable. 57 | 58 | > 12. What temporary identifiers do the features in this specification create or 59 | > expose to the web? 60 | 61 | No temporary identifiers are created or exposed. 62 | 63 | > 13. How does this specification distinguish between behavior in first-party and 64 | > third-party contexts? 65 | 66 | TODO: Answer this question. Based on #8. 67 | 68 | > 14. How do the features in this specification work in the context of a browser’s 69 | > Private Browsing or Incognito mode? 70 | 71 | This API works the same in private browsing mode as it does in normal browsing mode. 72 | 73 | > 15. Does this specification have both "Security Considerations" and "Privacy 74 | > Considerations" sections? 75 | 76 | Yes (currently combined into one section) 77 | 78 | > 16. Do features in your specification enable origins to downgrade default 79 | > security protections? 80 | 81 | No. 82 | 83 | > 17. What happens when a document that uses your feature is kept alive in BFCache 84 | > (instead of getting destroyed) after navigation, and potentially gets reused 85 | > on future navigations back to the document? 86 | 87 | BFCache has no impact on this API. 88 | 89 | > 18. What happens when a document that uses your feature gets disconnected? 90 | 91 | The override will persist until the user clears their preference or the user agent clears the preference. 92 | 93 | Therefor when the document reloads the override will still be in place. 94 | 95 | > 19. What should this questionnaire have asked? 96 | 97 | Questions above covered things well. 98 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The web preferences API has moved to the CSS WG 5 | 6 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [80485] 3 | , "contacts": ["yoavweiss"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------