<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas Steiner</title>
    <description>The latest articles on DEV Community by Thomas Steiner (@tomayac).</description>
    <link>https://dev.to/tomayac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F83996%2F193f036c-e51b-4126-bd70-d673810979b6.jpeg</url>
      <title>DEV Community: Thomas Steiner</title>
      <link>https://dev.to/tomayac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomayac"/>
    <language>en</language>
    <item>
      <title>Web Apps on macOS Sonoma 14 Beta</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Wed, 07 Jun 2023 13:39:59 +0000</pubDate>
      <link>https://dev.to/tomayac/web-apps-on-macos-sonoma-14-beta-83i</link>
      <guid>https://dev.to/tomayac/web-apps-on-macos-sonoma-14-beta-83i</guid>
      <description>&lt;h2&gt;
  
  
  Executive summary &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#executive-summary"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With macOS Sonoma, &lt;strong&gt;Apple goes all-in on the concept of installable web apps&lt;/strong&gt;. They're highly integrated in the overall macOS experience and don't give away their web roots by not showing any Safari UI at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing environment &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#testing-environment"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Tested on macOS Sonoma 14.0 Beta (&lt;code&gt;23A5257q&lt;/code&gt;) with Safari version 17.0 (&lt;code&gt;19616.1.14.11.11&lt;/code&gt;). It probably doesn't matter, but the testing device was a 13-inch, M1, 2020 MacBook Pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install experience &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#install-experience"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;On macOS Sonoma, you can add a website—any website, not just apps with a manifest—to your Dock. Go to the &lt;strong&gt;Share&lt;/strong&gt; icon and click &lt;strong&gt;Add to Dock&lt;/strong&gt; , or use the menu item &lt;strong&gt;File &amp;gt; Add to Dock&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kBH6Awr7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--u0q7vo9587s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kBH6Awr7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--u0q7vo9587s.png" alt="Adding an app via the Share icon." width="361" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adding an app via the &lt;strong&gt;Share&lt;/strong&gt; icon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xnBDPphp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--9zgyksdtzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xnBDPphp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--9zgyksdtzr.png" alt="Adding an app via the File menu." width="453" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adding an app via the &lt;strong&gt;File&lt;/strong&gt; menu.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can adjust the name and icon if desired. The URL is the URL you're on for pages without a manifest, or the &lt;code&gt;start_url&lt;/code&gt; for pages with a manifest. It can't be changed. For pages without an icon, Safari will create a fallback icon based on the first letter of the page's title.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Unlike on iOS/iPadOS, you can't add the same app twice, unless you rename it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H873hvMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--jbz4jqyyylc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H873hvMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--jbz4jqyyylc.png" alt="App name and icon are adjustable, the URL is not." width="708" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;App name and icon are adjustable, the URL is not.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The web app icon then appears in your Dock. Maskable icons are supported, and the typical macOS squircle shape is respected. Closing all windows of an app leaves the app running, aligned with macOS UX paradigms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Unlike on Chrome, the app doesn't launch immediately and "morph" from in-tab to in-app, but instead you remain on the tab and need to launch the app manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hr6ISpU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--1iikfvhhzcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hr6ISpU3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--1iikfvhhzcr.png" alt="Web app added to the Dock." width="404" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web app added to the Dock.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When right-clicking the Dock icon, you can uncheck &lt;strong&gt;Keep in Dock&lt;/strong&gt; and still launch the app via Launchpad, Spotlight Search, or even just by double-clicking the app icon in &lt;code&gt;~/Applications/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch experience &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#launch-experience"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The out-of-the box launch experience of web apps is fantastic. Nowhere does it give away that this is a web app. For apps with a manifest, there's no Safari UI whatsoever, and the expectation is that such apps are single-page apps that provide their own navigation controls. If an app is well made, lay persons probably wouldn't be able to tell that something is a web app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1RbvBG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--hysefdcnykc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1RbvBG0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--hysefdcnykc.png" alt="Web app running without any Safari UI." width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web app running without any Safari UI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Different from iOS/iPadOS, credentials in cookies are copied over, so if you were logged in when running in the tab, you're logged in when you launch the app. No other storage means apart from cookies are copied. &lt;em&gt;"When a user adds a website to their Dock, Safari will copy the website's cookies to the web app. That way, if someone is logged into their account in Safari, they will remain logged in within the web app. This will only work if the authentication state is stored within cookies. Safari does not copy over any other kind of local storage. After a user adds a web app to the Dock, no other website data is shared, which is great for privacy".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Web Inspector (DevTools) is blocked, even with the &lt;strong&gt;Show features for web developers&lt;/strong&gt; checkbox checked. There's no &lt;strong&gt;Develop&lt;/strong&gt; menu item nor can you right-click and &lt;strong&gt;Inspect Element&lt;/strong&gt;. This looks like a conscious decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Extensions don't run and likewise aren't displayed. Also probably a conscious decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Same-origin links are handled in-app, cross-origin links open in the default browser.&lt;/p&gt;

&lt;p&gt;If a user navigates to an already installed app in Safari, a prompt is displayed that invites the user to &lt;strong&gt;Open in web app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--953L9xyT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--k85cr23x5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--953L9xyT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--k85cr23x5v.png" alt="Prompt inviting the user to Open in web app." width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prompt inviting the user to &lt;strong&gt;Open in web app&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  macOS integration experience &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#macos-integration-experience"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Web apps on Mac let you focus on the websites you use all the time, separate from the rest of your browsing. Like all Mac apps, web apps work great with Stage Manager, Mission Control, and keyboard shortcuts like Command + Tab. Web apps can be opened from the Dock, Launchpad, and Spotlight Search.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IsTb-An9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--6e18tk2ywgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IsTb-An9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--6e18tk2ywgl.png" alt="Multitasking experience." width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multitasking experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--85o2Q8aR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gyygf94lo0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--85o2Q8aR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gyygf94lo0l.png" alt="Spotlight search experience." width="611" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spotlight search experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1fxhCMTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--cwy5ftpy4qi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1fxhCMTg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--cwy5ftpy4qi.png" alt="Launchpad experience." width="511" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Launchpad experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wVZxrLFk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--wg589k8r7mh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wVZxrLFk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--wg589k8r7mh.png" alt="Stage Manager experience." width="228" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Stage Manager experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3EXLOxj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--n1elzn6cq9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3EXLOxj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--n1elzn6cq9n.png" alt="All web apps have an About dialog." width="396" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All web apps have an &lt;strong&gt;About&lt;/strong&gt; dialog.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Settings and permissions &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#settings-and-permissions"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Web apps work with AutoFill credentials from iCloud Keychain and from third-party apps that have adopted the&lt;a href="https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_authentication-services_autofill-credential-provider"&gt;Credential Provider Extension API&lt;/a&gt;. Users can grant permission to a web app to use their camera, microphone and location in the same way they grant such permissions to other Mac apps through system prompts and the Privacy &amp;amp; Security section of System Settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2ispLAyD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--2rirguybc3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2ispLAyD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--2rirguybc3w.png" alt="System settings with Camera permissions." width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;System settings with Camera permissions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Web apps on Mac support&lt;a href="https://developer.apple.com/documentation/usernotifications/sending_web_push_notifications_in_web_apps_safari_and_other_browsers?language=objc"&gt;web push&lt;/a&gt;,&lt;a href="https://webkit.org/blog/14112/badging-for-home-screen-web-apps/"&gt;badging&lt;/a&gt;, and all the usual web standards implemented by WebKit, just like web apps&lt;a href="https://webkit.org/blog/13878/web-push-for-web-apps-on-ios-and-ipados/"&gt;on iOS and iPadOS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; There seems to be a bug where the hosting Web App appears as the app requesting the Notifications permission. Notifications then work as expected, though, including using the correct icon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MEVW7QOI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f4adwfi8t1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MEVW7QOI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f4adwfi8t1t.png" alt="Notifications permission prompt with the wrong app name and icon." width="576" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notifications permission prompt with the wrong app name and icon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Web apps have their own &lt;strong&gt;Settings&lt;/strong&gt; dialog. In &lt;strong&gt;General&lt;/strong&gt; , the app name and icon can be changed and navigation controls can be toggled on or off. The&lt;a href="https://developer.apple.com/documentation/safari-release-notes/safari-15-release-notes#:~:text=Added%20support%20for%20the%20theme%2Dcolor%20meta%20tag%20to%20change%20the%20tab%20bar%20background%20and%20over%2Dscroll%20area%20in%20macOS%20and%20iPadOS%2C%20and%20the%20status%20bar%20in%20iOS."&gt;theming behavior of the title bar&lt;/a&gt;can be changed, too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Navigation controls are toggled off when there's a manifest with &lt;code&gt;"display": "standalone"&lt;/code&gt;. In all other cases, even if a manifest exists but with a different &lt;code&gt;"display"&lt;/code&gt; mode,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4DWvruT---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--kgsu98ksrqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4DWvruT---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--kgsu98ksrqf.png" alt="Web app Settings dialog on the General tab." width="739" height="426"&gt;&lt;/a&gt;&lt;em&gt;Web app &lt;strong&gt;Settings&lt;/strong&gt; dialog on the &lt;strong&gt;General&lt;/strong&gt; tab.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; There's currently a &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257806"&gt;bug&lt;/a&gt; where web apps don't correctly report &lt;code&gt;matchMedia('(display-mode: standalone)')&lt;/code&gt;. Added to the Dock web apps think they run in a tab.&lt;/p&gt;

&lt;p&gt;With navigation controls enabled, there's an &lt;strong&gt;Open in Safari&lt;/strong&gt; icon in the upper right corner. Despite its label, it actually respects the user's default browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zglft2O2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--c95fwgkg6e9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zglft2O2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--c95fwgkg6e9.png" alt="Open in Safari icon." width="305" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;_ &lt;strong&gt;Open in Safari&lt;/strong&gt; icon._&lt;/p&gt;

&lt;p&gt;Web pages get navigation affordances in the form of a back and forward button. There's no reload button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h1K_ylH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f5jaczcwma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h1K_ylH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f5jaczcwma.png" alt="Back and forward buttons." width="175" height="102"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Back and forward buttons.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; With navigation controls toggled to off, the title of the web app sourced from the manifest is not shown. With navigation controls toggled to on, the title sourced from the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; is shown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; When you right-click, there's a context menu with &lt;strong&gt;Reload&lt;/strong&gt; or &lt;strong&gt;Back&lt;/strong&gt; and &lt;strong&gt;Reload&lt;/strong&gt;. This works independent from whether navigation controls are toggled on or off.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Privacy&lt;/strong&gt; tab allows the user to clear website data and links into the &lt;strong&gt;Privacy &amp;amp; Security Settings&lt;/strong&gt; of the System Settings app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--peLlkWGd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f37x3wpvl5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--peLlkWGd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--f37x3wpvl5.png" alt="Web app Settings dialog on the Privacy tab." width="739" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web app &lt;strong&gt;Settings&lt;/strong&gt; dialog on the &lt;strong&gt;Privacy&lt;/strong&gt; tab.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical analysis &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#technical-analysis"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;All apps are stored in &lt;code&gt;~/Applications/&lt;/code&gt;. The package contents of each apps are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;_CodeSignature&lt;/code&gt; folder with code signature metadata&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;Resources&lt;/code&gt; folder with just the app icons as a single&lt;code&gt;ApplicationIcon.icns&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;an &lt;code&gt;Info.plist&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--USZj9hnY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--oekactyqlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--USZj9hnY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--oekactyqlf.png" alt="The package contents of an app." width="800" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The package contents of an app.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Info.plist&lt;/code&gt; file interestingly contains an XML version of key parts of the manifest and metadata about the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
  &amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;CFBundleIconFile&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;ApplicationIcon&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleIdentifier&amp;lt;/key&amp;gt;
    &amp;lt;string
      &amp;gt;com.apple.Safari.WebApp.svgco.de.53298B34-AF7F-4074-9CA9-1EE46B7E3E83&amp;lt;/string
    &amp;gt;
    &amp;lt;key&amp;gt;CFBundleInfoDictionaryVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;6.0&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleName&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;SVGcode&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundlePackageType&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;AAPL&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleShortVersionString&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;1.0&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;CFBundleSupportedPlatforms&amp;lt;/key&amp;gt;
    &amp;lt;array&amp;gt;
      &amp;lt;string&amp;gt;MacOSX&amp;lt;/string&amp;gt;
    &amp;lt;/array&amp;gt;
    &amp;lt;key&amp;gt;CFBundleURLTypes&amp;lt;/key&amp;gt;
    &amp;lt;array&amp;gt;
      &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;CFBundleURLSchemes&amp;lt;/key&amp;gt;
        &amp;lt;array&amp;gt;
          &amp;lt;string&amp;gt;x-webkit-app-launch&amp;lt;/string&amp;gt;
        &amp;lt;/array&amp;gt;
        &amp;lt;key&amp;gt;LSHandlerRank&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;None&amp;lt;/string&amp;gt;
      &amp;lt;/dict&amp;gt;
    &amp;lt;/array&amp;gt;
    &amp;lt;key&amp;gt;CFBundleVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;1&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;LSMinimumSystemVersion&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;14.0&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;LSTemplateApplication&amp;lt;/key&amp;gt;
    &amp;lt;true /&amp;gt;
    &amp;lt;key&amp;gt;LSTemplateApplicationParameters&amp;lt;/key&amp;gt;
    &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;CFBundleIdentifier&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;com.apple.Safari.WebApp&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;TemplateAppUUID&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;53298B34-AF7F-4074-9CA9-1EE46B7E3E83&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;defaultarguments&amp;lt;/key&amp;gt;
      &amp;lt;true /&amp;gt;
      &amp;lt;key&amp;gt;teamIdentifier&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
    &amp;lt;key&amp;gt;Manifest&amp;lt;/key&amp;gt;
    &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;description&amp;lt;/key&amp;gt;
      &amp;lt;string
        &amp;gt;SVGcode is a Progressive Web App that lets you convert raster images
        like JPG, PNG, GIF, WebP, AVIF, etc. to vector graphics in SVG
        format.&amp;lt;/string
      &amp;gt;
      &amp;lt;key&amp;gt;display&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;standalone&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;icons&amp;lt;/key&amp;gt;
      &amp;lt;array&amp;gt;
        &amp;lt;dict&amp;gt;
          &amp;lt;key&amp;gt;purpose&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;maskable&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;sizes&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;1024x1024&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;src&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;https://svgco.de/favicon.png&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;type&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;image/png&amp;lt;/string&amp;gt;
        &amp;lt;/dict&amp;gt;
        &amp;lt;dict&amp;gt;
          &amp;lt;key&amp;gt;purpose&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;any&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;sizes&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;150x150&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;src&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;https://svgco.de/favicon.svg&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;type&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;image/svg+xml&amp;lt;/string&amp;gt;
        &amp;lt;/dict&amp;gt;
        &amp;lt;dict&amp;gt;
          &amp;lt;key&amp;gt;purpose&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;monochrome&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;sizes&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;150x150&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;src&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;https://svgco.de/favicon-bw.svg&amp;lt;/string&amp;gt;
          &amp;lt;key&amp;gt;type&amp;lt;/key&amp;gt;
          &amp;lt;string&amp;gt;image/svg+xml&amp;lt;/string&amp;gt;
        &amp;lt;/dict&amp;gt;
      &amp;lt;/array&amp;gt;
      &amp;lt;key&amp;gt;name&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;SVGcode&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;scope&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;https://svgco.de&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;short_name&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;SVGcode&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;start_url&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;https://svgco.de/&amp;lt;/string&amp;gt;
      &amp;lt;key&amp;gt;theme_color&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;#ffffff&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
    &amp;lt;key&amp;gt;WKPushBundleMetadata&amp;lt;/key&amp;gt;
    &amp;lt;dict&amp;gt;
      &amp;lt;key&amp;gt;manifestId&amp;lt;/key&amp;gt;
      &amp;lt;string&amp;gt;https://svgco.de/&amp;lt;/string&amp;gt;
    &amp;lt;/dict&amp;gt;
  &amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to iOS/iPadOS, web apps run in the context of a separate process called&lt;code&gt;Web App.app&lt;/code&gt;, which resides in&lt;code&gt;/System/Volumes/Preboot/Cryptexes/App/System/Library/CoreServices/Web App.app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👀 Observation:&lt;/strong&gt; Separating Safari and Web App allows both to run independently. You can open a Web app without opening Safari, you can close Safari without all web apps closing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G6vqb1yZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--3cd4wjo74qu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G6vqb1yZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--3cd4wjo74qu.png" alt="The Web App.app app in Finder." width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The &lt;code&gt;Web App.app&lt;/code&gt; app in Finder.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each web app runs as its own process of kind &lt;code&gt;Web&lt;/code&gt;, accompanied by a number of helper processes of kind &lt;code&gt;Apple&lt;/code&gt;. They can all be seen in Activity Monitor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iJEhknlV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--cln1h5jyf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iJEhknlV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--cln1h5jyf1.png" alt="Activity Monitor showing all processes associated with a web app." width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Activity Monitor showing all processes associated with a web app.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wish list for Apple &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#wish-list-for-apple"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;(Also see &lt;a href="https://docs.google.com/document/d/1chkEulpKBNQQfGIgTfQSbJhjw5mtZhvE47PF4tyTEXc/edit#"&gt;Most wanted PWA features on iOS/iPadOS/macOS Safari&lt;/a&gt;.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for the&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API"&gt;File System Access API&lt;/a&gt;, so users can install apps and edit files directly, rather than opening them and then downloading a copy. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=231706"&gt;🧭 231706&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Add support for&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFileSystemHandle"&gt;&lt;code&gt;DataTransferItem.getAsFileSystemHandle()&lt;/code&gt;&lt;/a&gt;, so users can drag and drop files into web apps and edit the file. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257792"&gt;🧭 257792&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Add support for the&lt;a href="https://developer.chrome.com/articles/web-share-target/"&gt;Web Share Target API&lt;/a&gt;, so you can share data to installed web apps. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=194593"&gt;🧭 194593&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Let web apps programmatically trigger the &lt;strong&gt;Add to Dock&lt;/strong&gt; flow, since the &lt;strong&gt;Share&lt;/strong&gt; icon and the &lt;strong&gt;File&lt;/strong&gt; menu install ways are hard to discover. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=255716"&gt;🧭 255716&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Add support for the&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window_Controls_Overlay_API"&gt;Window Controls Overlay API&lt;/a&gt;, so the gorgeous current experience can be made even more gorgeous. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257782"&gt;🧭 257782&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qcnGB2xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--4dq3oazo75q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qcnGB2xi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--4dq3oazo75q.png" alt="Spotify native app title bar experience." width="569" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spotify native app title bar experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PatFlid6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--9bwtu16pbtv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PatFlid6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--9bwtu16pbtv.png" alt="Spotify web app title bar experience." width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spotify web app title bar experience.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for the&lt;a href="https://github.com/WICG/file-handling/blob/main/explainer.md"&gt;File Handling API&lt;/a&gt;, so web apps can open files from Finder by double click if the web app is registered as the default file handler for a given file type, or by right click and then &lt;strong&gt;Open with&lt;/strong&gt; if the web app can handle a file type, but isn't the default file handler. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257783"&gt;🧭 257783&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Add support for the&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Launch_Handler_API"&gt;Launch Handler API&lt;/a&gt;, so web apps can decide how they want to handle launch events. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257785"&gt;🧭 257785&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Reflect the cookie-copying logic on iOS/iPadOS. It's a very frustrating experience if you have to log in twice, even more so if two-factor authentication is involved. [&lt;a href="https://bugs.webkit.org/show_bug.cgi?id=257786"&gt;🧭 257786&lt;/a&gt;]&lt;/li&gt;
&lt;li&gt;Allow users to turn off the &lt;strong&gt;Open in web app&lt;/strong&gt; prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommendations for Chrome &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#recommendations-for-chrome"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Better respect macOS' design paradigms. Currently web app icon handling looks not integrated and icon shapes are all over the place. The examples below are all web apps installed via Chrome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--drGe83g2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--pp6i6yqgm9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--drGe83g2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--pp6i6yqgm9l.png" alt="Web app icon shapes installed from Chrome don't respect the squircle." width="490" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web app icon shapes installed from Chrome don't respect the squircle.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move the extension puzzle piece and the Window Controls Overlay chevron into the three dots menu. Web apps can look so much cleaner without both in plain sight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WFgX3_BZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gqdtdnumc98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WFgX3_BZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gqdtdnumc98.png" alt="Window Controls Overlay chevron and extension puzzle piece clutter the UI of&amp;lt;br&amp;gt;
Chrome-installed apps." width="173" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Window Controls Overlay chevron and extension puzzle piece clutter the UI of Chrome-installed apps.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New Fugu API needs &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#new-fugu-api-needs"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With Chrome and Safari now allowing web apps to be installed on macOS, it would be fantastic if installed apps could respect macOS UX guidelines and populate the system-level menu. Ideally Apple and Google engage jointly on the corresponding Project Fugu 🐡 API request tracked in&lt;a href="https://crbug.com/1295253"&gt;crbug/1295253&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wijOeuUR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gg6jy7x6mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wijOeuUR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/images/webappsonmacos--gg6jy7x6mg.png" alt="Web app default menu." width="450" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Web app default menu.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#conclusion"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Web apps in macOS Sonoma 14 Beta seamlessly integrate into the macOS experience, with no or very little visible Safari UI and with support for various operating system features. There is an enormous potential for web apps on macOS to succeed, and if Apple only works on a third of the items on my wish list, the potential is even bigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related links &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/#related-links"&gt;🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webkit.org/blog/14205/news-from-wwdc23-webkit-features-in-safari-17-beta/#web-apps"&gt;News from WWDC23: WebKit Features in Safari 17 beta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2023/10120/"&gt;What's new in web apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9cKIbO2O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/feed.php%3Fdl%3Dhttps%253A%252F%252Fblog.tomayac.com%252F2023%252F06%252F07%252Fweb-apps-on-macos-sonoma-14-beta%252F%26dp%3D%252F2023%252F06%252F07%252Fweb-apps-on-macos-sonoma-14-beta%252F%26dt%3DWeb%2520Apps%2520on%2520macOS%2520Sonoma%252014%2520Beta" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9cKIbO2O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://blog.tomayac.com/feed.php%3Fdl%3Dhttps%253A%252F%252Fblog.tomayac.com%252F2023%252F06%252F07%252Fweb-apps-on-macos-sonoma-14-beta%252F%26dp%3D%252F2023%252F06%252F07%252Fweb-apps-on-macos-sonoma-14-beta%252F%26dt%3DWeb%2520Apps%2520on%2520macOS%2520Sonoma%252014%2520Beta" alt="Thomas Steiner" width="64" height="64"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
This post appeared first on &lt;a href="https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/"&gt;https://blog.tomayac.com/2023/06/07/web-apps-on-macos-sonoma-14-beta/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>macossonoma</category>
      <category>progressivewebapps</category>
    </item>
    <item>
      <title>Project Fugu 🐡 at W3C TPAC</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Sat, 21 Sep 2019 16:03:15 +0000</pubDate>
      <link>https://dev.to/tomayac/project-fugu-at-w3c-tpac-2ngi</link>
      <guid>https://dev.to/tomayac/project-fugu-at-w3c-tpac-2ngi</guid>
      <description>&lt;p&gt;This week, I attended my now third W3CTPAC. After &lt;a href="https://www.w3.org/2017/11/TPAC/Overview.html"&gt;TPAC 2017&lt;/a&gt; in Burlingame, CA, United States of America and &lt;a href="https://www.w3.org/2018/10/TPAC/"&gt;TPAC 2018&lt;/a&gt; in Lyon, France, &lt;a href="https://www.w3.org/2019/09/TPAC/Overview.html"&gt;TPAC 2019&lt;/a&gt; was held in Fukuoka, Japan. For the first time, I felt like I could &lt;em&gt;somewhat meaningfully&lt;/em&gt; contribute and had at least a &lt;em&gt;baseline understanding&lt;/em&gt; of the underlying W3C mechanics. As each year, the &lt;a href="https://www.w3.org/2019/09/TPAC/schedule.html"&gt;TPAC agenda&lt;/a&gt; was crammed and overlaps were unavoidable. Below is the write-up of the meetings I had time to attend.&lt;/p&gt;

&lt;h4&gt;
  
  
  Day 1
&lt;/h4&gt;

&lt;p&gt;On Monday, I attended the &lt;a href="https://www.w3.org/sw/"&gt;Service Workers Working Group&lt;/a&gt; (WG) meeting. The &lt;a href="https://github.com/w3c/ServiceWorker/issues/1460#issue-482168365"&gt;agenda&lt;/a&gt; this time was a mix of implementor updates, new proposals, and a lot of discussion of special cases. I know &lt;a href="https://twitter.com/jaffathecake"&gt;Jake Archibald&lt;/a&gt; is working on a summary post, so I leave it to him to summarize the day. The&lt;a href="https://docs.google.com/document/d/1q090ovJ4gd8wSfVtvuoZLMZ51YkiFDsEZ0Jiqi41Iys/edit"&gt;raw meeting minutes&lt;/a&gt; are available in case you're interested.&lt;/p&gt;

&lt;h4&gt;
  
  
  Day 2
&lt;/h4&gt;

&lt;p&gt;On Tuesday, I visited the&lt;a href="https://www.w3.org/2011/webappsec/"&gt;Web Application Security Working Group&lt;/a&gt; meeting as an observer. I was mostly interested in this WG because the&lt;a href="https://github.com/w3c/webappsec/blob/master/meetings/2019/2019-09-TPAC-agenda.md"&gt;agenda&lt;/a&gt; promised interesting proposals like Apple's&lt;a href="https://wicg.github.io/change-password-url/index.html"&gt;&lt;code&gt;/.well-known/change-password&lt;/code&gt;&lt;/a&gt;that was met with universal agreement. Some interesting discussion also sparked around again Apple's&lt;a href="https://lists.w3.org/Archives/Public/public-webappsec/2019Sep/0004.html"&gt;&lt;code&gt;isLoggedIn()&lt;/code&gt;&lt;/a&gt; API proposal. I was reminded of why on the web we can't have nice things through an attack vector that leverages HSTS for tracking purposes. Luckily there is&lt;a href="https://webkit.org/blog/8146/protecting-against-hsts-abuse/"&gt;browser mitigation&lt;/a&gt; in place to prevent this.The&lt;a href="https://github.com/w3c/webappsec/blob/master/meetings/2019/2019-09-TPAC-minutes.md"&gt;meeting minutes&lt;/a&gt; cover the entire day.&lt;/p&gt;

&lt;h4&gt;
  
  
  Day 3
&lt;/h4&gt;

&lt;p&gt;Wednesday was unconference day with&lt;a href="https://w3c.github.io/tpac-breakouts/sessions.html"&gt;59(!) breakout sessions&lt;/a&gt;. Other than the at times tedious working group sessions,I find breakout sessions to be oftentimes more interesting and an opportunity to learn new things.&lt;/p&gt;

&lt;h5&gt;
  
  
  Breakout Session JS Built-in Modules
&lt;/h5&gt;

&lt;p&gt;The first breakout session I attended was on&lt;a href="https://w3c.github.io/tpac-breakouts/sessions.html#jsbuiltin"&gt;JS built-in modules&lt;/a&gt;, a TC39 proposal by Apple for a&lt;a href="https://github.com/tc39/proposal-javascript-standard-library"&gt;JavaScript Built-in Library&lt;/a&gt;. The &lt;a href="https://www.w3.org/2019/09/18-jsbuiltin-minutes.html"&gt;session's minutes&lt;/a&gt; are available, in general there was a lot of discussion and disagreement around namespaces and how built-in modules should be governed.&lt;/p&gt;

&lt;h5&gt;
  
  
  Breakout Session New Module Types: JSON, CSS, and HTML
&lt;/h5&gt;

&lt;p&gt;The next session was on&lt;a href="https://w3c.github.io/tpac-breakouts/sessions.html#new-modules"&gt;new module types for JSON, CSS, and HTML&lt;/a&gt;. As the developer of&lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt;, I'm fully in favor of getting rid of the clumsy&lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle/blob/bf737bed7a7d3ba5086585a94578ed814500bb6c/src/dark-mode-toggle.mjs#L75-L249"&gt;&lt;code&gt;innerHTML&lt;/code&gt; all the things!!!1!&lt;/a&gt; approach that vanilla JS custom elements currently make the programmer to follow. If you're likewise interested, subscribe to the&lt;a href="https://github.com/w3c/webcomponents/issues/759"&gt;CSS Modules issue&lt;/a&gt; and the &lt;a href="https://github.com/w3c/webcomponents/issues/645"&gt;HTML Modules issue&lt;/a&gt; in the Web Components WG repo. The discussion circulated mostly around details how &lt;code&gt;@imports&lt;/code&gt; would work and how to convey the type of the import to avoid security issues, for example following the &lt;code&gt;&amp;lt;link rel="preload"&amp;gt;&lt;/code&gt; way. The &lt;a href="https://www.w3.org/2019/09/18-new-modules-minutes.html"&gt;meeting minutes&lt;/a&gt; have the full details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Non-working example&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styles.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;stylesheet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settings.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Breakout Session Mini App Standardization
&lt;/h5&gt;

&lt;p&gt;The &lt;a href="https://w3c.github.io/tpac-breakouts/sessions.html#miniapp"&gt;Mini App Standardization&lt;/a&gt; session, organized by the &lt;a href="https://www.w3.org/2018/chinese-web-ig/"&gt;Chinese Web Interest Group&lt;/a&gt;, was super interesting to me.In preparation of the&lt;a href="https://events.google.cn/intl/zh-CN/developerdays2019/"&gt;Google Developer Days in Shanghai, China&lt;/a&gt;, that I spoke at right before TPAC, I have&lt;a href="https://blog.tomayac.com/2019/08/15/a-quick-look-at-wechats-mini-programs/"&gt;looked at WeChat mini programs&lt;/a&gt; and documented the developer experience and how close to and yet how far from the web they are. A couple of days before TPAC, the Chinese Web Interest Group had released a&lt;a href="https://www.w3.org/TR/mini-app-white-paper/"&gt;white paper&lt;/a&gt; that documents their ideas. The success the various mini apps platforms have achieved deserves our full respect. There were, however, various voices—including from theTAG—that urged the various stakeholders to converge their work with efforts made in the area ofProgressive Web Apps, for example around the &lt;a href="https://w3c.github.io/manifest/"&gt;Web App Manifest&lt;/a&gt; rather than create yet another manifest-like format. Read the &lt;a href="https://www.w3.org/2019/09/18-miniapp-minutes.html"&gt;full session minutes&lt;/a&gt; for all details. One of the results of the session was the creation of the&lt;a href="https://www.w3.org/community/miniapps/"&gt;MiniApps Ecosystem Community Group&lt;/a&gt; that I hope to join.&lt;/p&gt;
&lt;h5&gt;
  
  
  Breakout Session For a More Capable Web—Project Fugu
&lt;/h5&gt;

&lt;p&gt;Together with &lt;a href="https://twitter.com/anssik"&gt;Anssi Kostiainen&lt;/a&gt; from Intel and&lt;a href="https://twitter.com/thejohnjansen"&gt;John Jansen&lt;/a&gt; from Microsoft,I organized a breakout session &lt;a href="https://w3c.github.io/tpac-breakouts/sessions.html#capable-web"&gt;for a more capable web&lt;/a&gt; under the umbrella of Project Fugu 🐡. You can see our slides embedded below. In the session we argue that to remain relevant with native, hybrid, or mini apps, web apps, too,need access to a comparable set of APIs. We briefly touched upon the APIs being worked on by the cross-company project partners,and then opened the floor for an open discussion on why we see the browser-accessible web in dangerif we don't move it forward now,despite all fully acknowledged challenges around privacy, security, and compatibility. You can follow the discussion in the excellent(!) &lt;a href="https://www.w3.org/2019/09/18-capable-web-minutes.html"&gt;session minutes&lt;/a&gt;, courtesy of Anssi.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/project-fugu-at-w3c-tpac?previewSize=100&amp;amp;path=index.html" alt="project-fugu-at-w3c-tpac on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;h4&gt;
  
  
  Day 4 and Day 5
&lt;/h4&gt;

&lt;p&gt;Thursday and Friday were dedicated to the &lt;a href="https://www.w3.org/das/"&gt;Devices and Sensor WG&lt;/a&gt;. The &lt;a href="https://github.com/w3c/devicesensors-wg/issues/24"&gt;agenda&lt;/a&gt; was not too packed, but still kept us busy for one and a half days. We discussed almost from the start about permissions and how they should be handled. Permissions are a big topic in Project Fugu 🐡 and I'm happy that there's work ongoing in the TAG to improve the situation, including efforts around the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API"&gt;Permissions API&lt;/a&gt; that is unfortunately not universally supported, leading to inconsistencies with some APIs having a static method for getting permission, &lt;a href="https://github.com/w3c/sensors/issues/388#issuecomment-532942477"&gt;others&lt;/a&gt; asking for permission upon the first usage attempt, and yet others to integrate with the Permissions API.For the &lt;a href="https://w3c.github.io/geolocation-sensor/"&gt;Geolocation Sensor API&lt;/a&gt;, we agreed to try retrofitting expressive configuration of foreground tracking into the &lt;a href="https://w3c.github.io/geolocation-api/"&gt;Geolocation API&lt;/a&gt; specification instead of doing it in Geolocation Sensor, which should improve vendor adoption. For geofencing and background geolocation tracking, we decided to explore &lt;a href="https://github.com/beverloo/notification-triggers"&gt;Notification Triggers&lt;/a&gt; and &lt;a href="https://w3c.github.io/wake-lock/"&gt;Wake Locks&lt;/a&gt; respectively, which both weren't options when the work on Geolocation Sensor was started initially.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sites.google.com/view/maryammjd/home"&gt;Maryam Mehrnezhad&lt;/a&gt;, an invited expert in the working group whose research is focused on privacy and security, presented on and discussed with us the implications on both fields that sensors potentially have and whether mitigation like accuracy bucketing or frequency capping are effective. The &lt;a href="https://www.w3.org/2019/09/19-dap-minutes.html#x18"&gt;minutes&lt;/a&gt; capture the conversation well.&lt;/p&gt;

&lt;p&gt;Finally, we &lt;a href="https://github.com/w3c/wake-lock/issues/226#issuecomment-533032056"&gt;changed the surface of the Wake Lock API&lt;/a&gt; hopefully for the last time. The previous changes just didn't feel right from a developer experience point of view, so better change the API while it's behind a flag than be sorry forever. I reckon I do feel sorry for the implementors &lt;a href="https://twitter.com/rijubrata"&gt;Rijubrata Bhaumik&lt;/a&gt; and &lt;a href="https://github.com/rakuco"&gt;Raphael Kubo da Costa&lt;/a&gt;… 🙇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;partial&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Navigator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SameObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="nx"&gt;WakeLock&lt;/span&gt; &lt;span class="nx"&gt;wakeLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;partial&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;WorkerNavigator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SameObject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="nx"&gt;WakeLock&lt;/span&gt; &lt;span class="nx"&gt;wakeLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Exposed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;DedicatedWorker&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;WakeLock&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unsigned&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WakeLockType&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsigned&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;wakeLockID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;dictionary&lt;/span&gt; &lt;span class="nx"&gt;WakeLockEventInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required&lt;/span&gt; &lt;span class="nx"&gt;unsigned&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;wakeLockID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Exposed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;DedicatedWorker&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;WakeLockEvent&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOMString&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WakeLockEventInit&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="nx"&gt;unsigned&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;long&lt;/span&gt; &lt;span class="nx"&gt;wakeLockID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As a general theme, we "hardened" a number of APIs, for example decided to integrate geolocation with &lt;a href="https://w3c.github.io/webappsec-feature-policy/"&gt;Feature Policy&lt;/a&gt; and now require a secure connection for the &lt;a href="https://w3c.github.io/battery/"&gt;Battery Status API&lt;/a&gt;. The chairs Anssi and &lt;a href="https://twitter.com/reillyeon"&gt;Reilly Grant&lt;/a&gt; have scribed the one and a half days brilliantly, the minutes for &lt;a href="https://www.w3.org/2019/09/19-dap-minutes.html"&gt;day 1&lt;/a&gt; and &lt;a href="https://www.w3.org/2019/09/20-dap-minutes.html"&gt;day 2&lt;/a&gt; are both online.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;As I wrote in the beginning, TPAC &lt;em&gt;slowly&lt;/em&gt; starts to feel like a venue where I can make some valuable contributions. &lt;a href="https://twitter.com/rowan_m"&gt;Rowan Merewood&lt;/a&gt; put it like this in a &lt;a href="https://twitter.com/rowan_m/status/1173808373436862464"&gt;tweet&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest thing I'm learning at [&lt;a href="https://twitter.com/hashtag/w3ctpac?src=hashtag_click"&gt;#W3Ctpac&lt;/a&gt;] is if you want to change the web, it's a surprisingly small group of people you need to convince. The surrounding appearance of the W3C and all its language is intimidating, but underneath it's just other human beings you can talk to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To which &lt;a href="https://twitter.com/kosamari"&gt;Mariko Kosaka&lt;/a&gt; fittingly &lt;a href="https://twitter.com/kosamari/status/1173811848518356993"&gt;responds&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[Y]]eah, but let's not forget getting to talk to that small set of people most often comes with being very, very, very privileged. […]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's indeed a &lt;em&gt;massive privilege&lt;/em&gt; to work for a company that has the money to take part in W3C activities, fly people across the world, and let them stay in five star conference hotels. With all the love for the web and all the great memories of a fantastic TPAC, let's not forget: the web is threatened from multiple angles, and being able to work in the standards bodies on defending it is a privilege of the few. Both shouldn't be the case.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Not All Is Nothing—Finding Out If the Browser Supports a Given Media Query</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Mon, 16 Sep 2019 00:19:54 +0000</pubDate>
      <link>https://dev.to/tomayac/not-all-is-nothing-finding-out-if-the-browser-supports-a-given-media-query-3an</link>
      <guid>https://dev.to/tomayac/not-all-is-nothing-finding-out-if-the-browser-supports-a-given-media-query-3an</guid>
      <description>&lt;p&gt;On the Google Chrome team, we're working on solving the interesting problem that some platforms like Windows 10 show a native &lt;em&gt;← Back&lt;/em&gt; button in the app window's title bar when the user has navigated into a standalone or fullscreen Progressive Web App (PWA). This can lead to PWAs inadvertently showing two means of in-app navigation: one from the operating system and one from the app itself. The screenshot below from Twitter's PWA illustrates this issue well. The two back buttons are highlighted in red, the Windows-generated button is in the title bar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ybYvZvdN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.tomayac.com/images/twitter-double-back-button.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ybYvZvdN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.tomayac.com/images/twitter-double-back-button.png" alt="Two back buttons in Twitter's PWA, one from Windows 10, one from the app."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order for PWAs to be able to detect the presence or absence of system-level navigation controls, we've proposed a new, aptly named CSS media query called &lt;code&gt;navigation-controls&lt;/code&gt;. The &lt;a href="https://github.com/fallaciousreasoning/backbutton-mediaquery/blob/master/explainer.md#css"&gt;Explainer&lt;/a&gt; goes into great detail on how this media query works, but the most basic way to use it can be seen in the code sample below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/**
 * Hide the app's own back button if the browser or the OS
 * provides any navigation controls.
 */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;navigation-controls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;#back-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This leads to the interesting question how developers can find out via JavaScript if a media query is supported and understood at all by a browser. Note that the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports"&gt;&lt;code&gt;@supports&lt;/code&gt; CSS at-rule&lt;/a&gt; can't be used to detect media query support. Before we dive into this question, let's get some things defined first.&lt;/p&gt;

&lt;h4&gt;
  
  
  Media types
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Media_types"&gt;Media types&lt;/a&gt; describe the general category of a device. Some examples are &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;screen&lt;/code&gt;, &lt;code&gt;speech&lt;/code&gt;, or simply the default media type &lt;code&gt;all&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Media features
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Media_features"&gt;Media features&lt;/a&gt; describe specific characteristics of the user agent, output device, or environment. Media feature expressions test for their presence or value. Examples are &lt;code&gt;prefers-color-scheme&lt;/code&gt;, &lt;code&gt;hover&lt;/code&gt;, and many more. Media feature expressions must always be surrounded by parentheses.&lt;/p&gt;

&lt;p&gt;Let's take a media feature expression like &lt;code&gt;(max-width: 600px)&lt;/code&gt;. From a JavaScript context, you can test if it matches as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(max-width: 600px)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Returns either `true` or `false`.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Some media features are evaluated in the&lt;a href="https://drafts.csswg.org/mediaqueries-5/#boolean-context"&gt;boolean context&lt;/a&gt;. This means that if the feature would be true for any value other than the number &lt;code&gt;0&lt;/code&gt;, a &lt;code&gt;&amp;lt;dimension&amp;gt;&lt;/code&gt; with the value &lt;code&gt;0&lt;/code&gt;, the keyword &lt;code&gt;none&lt;/code&gt;, or a value explicitly defined by that media feature to evaluate as false in a boolean context, the media feature evaluates to true. This is oftentimes a convenient shorthand for features like &lt;code&gt;hover&lt;/code&gt;that accepts the two values &lt;code&gt;none&lt;/code&gt; and &lt;code&gt;hover&lt;/code&gt;. Usually you don't mind for the &lt;code&gt;none&lt;/code&gt; value, but instead want to make sure you only use hover events if the user agent has some kind of hoverable device like a mouse. In this case, you can simply test for &lt;code&gt;@media (hover) { … }&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing if a browser understands a media query
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia"&gt;&lt;code&gt;window.matchMedia()&lt;/code&gt;&lt;/a&gt; method returns a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList"&gt;&lt;code&gt;MediaQueryList&lt;/code&gt;&lt;/a&gt; whose &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/media"&gt;&lt;code&gt;media&lt;/code&gt;&lt;/a&gt; read-only property is a &lt;code&gt;DOMString&lt;/code&gt; representing a serialized media query. What this means in practice is that we can use this to normalize media query expression strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Note the superfluous spaces and the '+' before the width&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;( max-width: +600px)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Returns `"(max-width: 600px)"`.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The newly proposed media feature &lt;code&gt;navigation-controls&lt;/code&gt;—apart from &lt;code&gt;back&lt;/code&gt;—has&lt;code&gt;none&lt;/code&gt; as one of its allowed keywords, and is evaluated in a boolean context. Bringing things together, we can thus check if the browser understands it by looking at the &lt;code&gt;media&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(navigation-controls)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Returns `"not all"` when the browser doesn't understand&lt;/span&gt;
&lt;span class="c1"&gt;// the media query, or `"(navigation-controls)"` else.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The interesting value here is &lt;code&gt;"not all"&lt;/code&gt;, meaning the browser doesn't understand the media feature. If the browser doesn't have the slightest clue what &lt;code&gt;navigation-controls&lt;/code&gt; is about, it tells you the serialization of this (unknown) media feature is something that will never match: the media &lt;em&gt;type&lt;/em&gt; &lt;code&gt;@media not all&lt;/code&gt;. Because &lt;code&gt;not all&lt;/code&gt; is &lt;a href="https://en.wikipedia.org/wiki/Nothing"&gt;nothing&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A quick look at WeChat's Mini Programs</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Thu, 15 Aug 2019 10:16:05 +0000</pubDate>
      <link>https://dev.to/tomayac/a-quick-look-at-wechat-s-mini-programs-1kba</link>
      <guid>https://dev.to/tomayac/a-quick-look-at-wechat-s-mini-programs-1kba</guid>
      <description>&lt;p&gt;While preparing for my &lt;a href="https://events.google.cn/intl/zh-CN/developerdays2019/agenda/#table-row-1-7" rel="noopener noreferrer"&gt;presentation&lt;/a&gt; at the &lt;a href="https://events.google.cn/intl/zh-CN/developerdays2019/" rel="noopener noreferrer"&gt;Google Developer Days 2019 in Shanghai, China&lt;/a&gt; I was reminded again that China is a market where few super apps like &lt;a href="https://www.wechat.com/en" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt; host a &lt;a href="https://techcrunch.com/2018/11/07/wechat-mini-apps-200-million-users/" rel="noopener noreferrer"&gt;gazillion mini apps or mini programs&lt;/a&gt; that fulfill everyday needs like booking cabs, reserving tables, etc.&lt;/p&gt;

&lt;p&gt;I got curious and &lt;a href="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html" rel="noopener noreferrer"&gt;downloaded the SDK&lt;/a&gt; and after playing a bit with the &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/" rel="noopener noreferrer"&gt;Your First Mini App tutorial&lt;/a&gt;, I realized the whole thing is so close to building for the actual web, it both fascinates, intrigues, and honestly somewhat infuriates me.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You style your apps with &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/view/wxss.html" rel="noopener noreferrer"&gt;WXSS&lt;/a&gt;, which is essentially CSS with some neat additions like &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/view/wxss.html" rel="noopener noreferrer"&gt;responsive pixels&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;page-section-gap&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="n"&gt;rpx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.page-body-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="n"&gt;rpx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You write your app logic with JavaScript (or TypeScript), with &lt;code&gt;App&lt;/code&gt; as the top level object and &lt;code&gt;wx&lt;/code&gt; as the object you get all the cool capabilities from. The &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/api/" rel="noopener noreferrer"&gt;API&lt;/a&gt; is incredibly powerful. Here are the keys of the &lt;code&gt;wx&lt;/code&gt; object:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addNativeDownloadTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addPhoneContact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addWeRunData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arrayBufferToBase64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64ToArrayBuffer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;batchGetContactDirectly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bindPaymentCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calRqt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canIUse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cancelDownloadAppTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvasGetImageData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvasPutImageData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvasToTempFilePath&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;captureScreen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkIsSoterEnrolledInDevice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkIsSupportFacialRecognition&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkIsSupportSoterAuthentication&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkSession&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseAddress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseContact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseImage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseInvoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseInvoiceTitle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseLocation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseMedia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseMessageFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseMultiMedia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseShareGroup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseVideo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chooseWeChatContact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clearStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;clearStorageSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closeBLEConnection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closeBluetoothAdapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closeSocket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloud&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compressImage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connectSocket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connectWifi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createAnimation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createAudioContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createBLEConnection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createCameraContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createCanvasContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createInnerAudioContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createIntersectionObserver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createInterstitialAd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createLivePlayerContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createLivePusherContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createMapContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createOffscreenCanvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createRewardedVideoAd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createSelectorQuery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createUDPSocket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createVideoContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createWorker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;downloadApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;downloadAppForIOS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;downloadFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;downloadSilkVoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drawCanvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;enterContact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exitVoIPChat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;faceVerifyForPay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getABTestConfig&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAccountInfoSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAppInstallState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAvailableAudioSources&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBLEDeviceCharacteristics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBLEDeviceServices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBackgroundAudioManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBackgroundAudioPlayerState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBackgroundFetchData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBackgroundFetchToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBatteryInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBatteryInfoSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBeacons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBluetoothAdapterState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getBluetoothDevices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getClipboardData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getConnectedBluetoothDevices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getConnectedWifi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getCookies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getExtConfig&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getExtConfigSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getFileInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getFileSystemManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getHCEState&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getImageInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getLabInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getLaunchOptionsSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getLocation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getLogManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getMenuButtonBoundingClientRect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getNetworkType&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getOpenDeviceId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getPublicLibVersion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getRealtimeLogManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getRecorderManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getResPath&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSavedFileInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSavedFileList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getScreenBrightness&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSelectedTextRange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSetting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getShareInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getStorageInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getStorageInfoSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getStorageSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSystemInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getSystemInfoSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getUpdateManager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getUserInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getWeRunData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getWifiList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getWxSecData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideKeyboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideNavigationBarLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideShareMenu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideTabBar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideTabBarRedDot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hideToast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;installDownloadApp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isSDKError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isSystemError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isThirdError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;joinVoIPChat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;launchApplicationDirectly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;launchApplicationForNative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;launchMiniProgram&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadFontFace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadPaySpeechDialectConfig&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;makePhoneCall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;makeVoIPCall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateBack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateBackApplication&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateBackH5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateBackMiniProgram&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateBackNative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateTo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateToDevMiniProgram&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateToMiniProgram&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigateToMiniProgramDirectly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nextTick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notifyBLECharacteristicValueChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notifyBLECharacteristicValueChanged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offAppHide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offAppShow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offAudioInterruptionBegin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offAudioInterruptionEnd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offLocalServiceDiscoveryStop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offLocalServiceFound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offLocalServiceLost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offLocalServiceResolveFail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offPageNotFound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;offWindowResize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAccelerometerChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppEnterBackground&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppEnterForeground&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppHide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppRoute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppRouteDone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppShow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAppUnhang&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAudioInterruptionBegin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onAudioInterruptionEnd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBLECharacteristicValueChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBLEConnectionStateChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBLEConnectionStateChanged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBackgroundAudioPause&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBackgroundAudioPlay&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBackgroundAudioStop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBackgroundFetchData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBeaconServiceChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBeaconUpdate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBluetoothAdapterStateChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onBluetoothDeviceFound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onCompassChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onDeviceMotionChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onDownloadAppStateChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onEvaluateWifi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onGetWifiList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onGyroscopeChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onHCEMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onKeyboardHeightChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onLocalServiceDiscoveryStop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onLocalServiceFound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onLocalServiceLost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onLocalServiceResolveFail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onLocationChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onMemoryWarning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onNativeEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onNetworkStatusChange&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onPageNotFound&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onPageReload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSocketClose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSocketError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSocketMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onSocketOpen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onTapNavigationBarRightButton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onUploadEncryptedFileToCDNProgress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onUserCaptureScreen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onVoIPChatInterrupted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onVoIPChatMembersChanged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onVoIPChatSpeakersChanged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onVoicePlayEnd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onWebviewEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onWifiConnected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onWindowResize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openBluetoothAdapter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openBusinessView&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openCard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openDeliveryList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openDocument&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openGoldenRedPacketDetail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openLocation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openMiniProgramHistoryList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openMiniProgramProfile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openMiniProgramSearch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openMiniProgramStarList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openOfficialAccountProfile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openOfflinePayView&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openQRCode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openSetting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openUrl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openUserProfile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openWCPayCardList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;operateWXData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pageScrollTo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pauseBackgroundAudio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pauseDownloadAppTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pauseVoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playBackgroundAudio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;playVoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;presetWifiList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preventApplePayUI&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;previewImage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;queryDownloadAppTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reLaunch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;readBLECharacteristicValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redirectTo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeSavedFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeStorageSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeTabBarBadge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;removeUserCloudStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reportAction&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reportAnalytics&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reportIDKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reportKeyValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reportMonitor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;request&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestMallPayment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestPayment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestPaymentToBank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requestVirtualPayment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resumeDownloadAppTask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;saveFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;saveImageToPhotosAlbum&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;saveVideoToPhotosAlbum&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scanCode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secureTunnel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;seekBackgroundAudio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendBizRedPacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendGoldenRedPacket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendHCEMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sendSocketMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setBackgroundColor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setBackgroundFetchToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setBackgroundTextStyle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setClipboardData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setCookies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setCurrentPaySpeech&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setEnableDebug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setInnerAudioOption&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setKeepScreenOn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setLabInfo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setNavigationBarAlpha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setNavigationBarColor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setNavigationBarRightButton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setNavigationBarTitle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setPageStyle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setResPath&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setScreenBrightness&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setStorageSync&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setTabBarBadge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setTabBarItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setTabBarStyle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setTopBarText&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setUserCloudStorage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setWifiList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shareAppMessageForFakeNative&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showActionSheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showModal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showNavigationBarLoading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showShareActionSheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showShareMenu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showTabBar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showTabBarRedDot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;showToast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startAccelerometer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startBeaconDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startBluetoothDevicesDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startCompass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startCustomFacialRecognitionVerify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startCustomFacialRecognitionVerifyAndUploadVideo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startDeviceMotionListening&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startFacialRecognitionVerify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startFacialRecognitionVerifyAndUploadVideo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startGyroscope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startHCE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startLocalServiceDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startLocationUpdate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startLocationUpdateBackground&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startPullDownRefresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startRecord&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startSoterAuthentication&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;startWifi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopAccelerometer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopBackgroundAudio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopBeaconDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopBluetoothDevicesDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopCompass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopDeviceMotionListening&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopGyroscope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopHCE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopLocalServiceDiscovery&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopLocationUpdate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopPullDownRefresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopRecord&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopVoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stopWifi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;switchTab&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;traceEvent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;triggerGettingWidgetData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updatePerfData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updateShareMenu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;updateVoIPChatMuteConfig&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploadEncryptedFileToCDN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploadFile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploadSilkVoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uploadWeRunData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;verifyPaymentPassword&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vibrateLong&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vibrateShort&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;voiceSplitJoint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;writeBLECharacteristicValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;You describe your interface with &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/view/wxml/" rel="noopener noreferrer"&gt;WXML&lt;/a&gt;, which is something between &lt;a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer"&gt;JSX&lt;/a&gt;, &lt;a href="https://vuejs.org/v2/guide/#Declarative-Rendering" rel="noopener noreferrer"&gt;Vue's declarative rendering&lt;/a&gt;; it also reminds me of old concepts like XSLT's &lt;a href="https://www.w3.org/TR/xslt-30/#xsl-if" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;xsl:if&amp;gt;&lt;/code&gt;&lt;/a&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;view&lt;/span&gt; &lt;span class="na"&gt;wx:if=&lt;/span&gt;&lt;span class="s"&gt;"{{view == 'WEBVIEW'}}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; WEBVIEW &lt;span class="nt"&gt;&amp;lt;/view&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;view&lt;/span&gt; &lt;span class="na"&gt;wx:elif=&lt;/span&gt;&lt;span class="s"&gt;"{{view == 'APP'}}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; APP &lt;span class="nt"&gt;&amp;lt;/view&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;view&lt;/span&gt; &lt;span class="na"&gt;wx:else=&lt;/span&gt;&lt;span class="s"&gt;"{{view == 'MINA'}}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; MINA &lt;span class="nt"&gt;&amp;lt;/view&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall a really nice separation of concerns. I could very well imagine being productive with this in no time. The onboarding experience of the documentation is pretty neat. The SDK is well made with (essentially an adapted) &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/devtools/devtools.html" rel="noopener noreferrer"&gt;Chrome DevTools integrated&lt;/a&gt; and some VS Code like features like code completion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming Concept
&lt;/h2&gt;

&lt;p&gt;The overall programming concept &lt;a href="http://opensocial.github.io/spec/2.5.1/Social-Gadget.xml#rfc.section.3.1.2" rel="noopener noreferrer"&gt;reminds of OpenSocial&lt;/a&gt; (if anyone remembers that) where there is a baseline assumption of a logged in user whose social graph can be explored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// OpenSocial&lt;/span&gt;
&lt;span class="nx"&gt;osapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;people&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewer&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your name is &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WeChat&lt;/span&gt;
&lt;span class="nx"&gt;wx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSetting&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authSetting&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scope.userInfo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;wx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserInfo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;        
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084473-8cf79900-bf4b-11e9-8838-447688caf661.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084473-8cf79900-bf4b-11e9-8838-447688caf661.png" alt="The "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Library
&lt;/h2&gt;

&lt;p&gt;They also have nice &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/component/" rel="noopener noreferrer"&gt;set of declarative components&lt;/a&gt;, think web components essentially, that you can create with WXML and interact with from JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;map&lt;/span&gt;
  &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myMap"&lt;/span&gt;
  &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 100%; height: 300px;"&lt;/span&gt;
  &lt;span class="na"&gt;latitude=&lt;/span&gt;&lt;span class="s"&gt;"{{latitude}}"&lt;/span&gt;
  &lt;span class="na"&gt;longitude=&lt;/span&gt;&lt;span class="s"&gt;"{{longitude}}"&lt;/span&gt;
  &lt;span class="na"&gt;markers=&lt;/span&gt;&lt;span class="s"&gt;"{{markers}}"&lt;/span&gt;
  &lt;span class="na"&gt;covers=&lt;/span&gt;&lt;span class="s"&gt;"{{covers}}"&lt;/span&gt;
  &lt;span class="na"&gt;show-location&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/map&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;23.099994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;113.324520&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;markers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;23.099994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;113.324520&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;T.I.T 创意园&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;covers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;23.099994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;113.344520&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;iconPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/image/location.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;23.099994&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;113.304520&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;iconPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/image/location.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onReady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapCtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMapContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myMap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;getCenterLocation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mapCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCenterLocation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084474-8d902f80-bf4b-11e9-9267-c69f4923e795.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084474-8d902f80-bf4b-11e9-9267-c69f4923e795.png" alt="WeChat  raw `&amp;lt;map&amp;gt;` endraw  component sample running in WeChat DevTools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And of course they have a &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/component/web-view.html" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;web-view&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084476-8d902f80-bf4b-11e9-9cb5-d3a216a4d4af.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084476-8d902f80-bf4b-11e9-9cb5-d3a216a4d4af.png" alt="WeChat  raw `&amp;lt;web-view&amp;gt;` endraw  component sample running in WeChat DevTools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inner Mechanics
&lt;/h2&gt;

&lt;p&gt;From what I understand, it's all running in an iframe, Chrome/WeChat DevTools then hides the container, and all you see is the WXML layer.&lt;br&gt;
When you look at the SDK's package contents, they make no real effort at hiding any of the inner mechanics: it's all HTML (of particular interest: &lt;code&gt;/Applications/wechatwebdevtools.app/Contents/Resources/package.nw/html/standalone.html&lt;/code&gt;), CSS (of particular interest: &lt;code&gt;/Applications/wechatwebdevtools.app/Contents/Resources/package.nw/static/app.css&lt;/code&gt;), and JavaScript (of particular interest: &lt;code&gt;/Applications/wechatwebdevtools.app/Contents/Resources/package.nw/js/vendor/index.js&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084475-8d902f80-bf4b-11e9-954a-aff06f9b0e8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F145676%2F63084475-8d902f80-bf4b-11e9-954a-aff06f9b0e8d.png" alt="Inspecting package contents of the WeChat DevTools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally there is a local web server running that allows them to link from the online docs to local URLs like &lt;a href="http://127.0.0.1:32123/minicode/VBZ3Jim26zYu" rel="noopener noreferrer"&gt;127.0.0.1:32123/minicode/VBZ3Jim26zYu&lt;/a&gt;, which in turn allows them to open code samples from the docs that are ready to play with a click in WeChat DevTools. The web server &lt;a href="https://medium.com/bugbountywriteup/zoom-zero-day-4-million-webcams-maybe-an-rce-just-get-them-to-visit-your-website-ac75c83f4ef5" rel="noopener noreferrer"&gt;luckily&lt;/a&gt; only runs  when the Wechat Devtools are open.&lt;/p&gt;

</description>
      <category>wechat</category>
      <category>wxml</category>
      <category>wxss</category>
    </item>
    <item>
      <title>Hello darkness, my old friend</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Mon, 05 Aug 2019 14:12:46 +0000</pubDate>
      <link>https://dev.to/chromiumdev/hello-darkness-my-old-friend-3jcg</link>
      <guid>https://dev.to/chromiumdev/hello-darkness-my-old-friend-3jcg</guid>
      <description>&lt;h4&gt;
  
  
  Overhyped or necessity? Learn everything about dark mode and how to support it to the benefit of your users!
&lt;/h4&gt;

&lt;p&gt;(Originally published on &lt;a href="https://web.dev/prefers-color-scheme/"&gt;https://web.dev/prefers-color-scheme/&lt;/a&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 I have done a lot of background research on the history and theory of dark mode, if you are only interested in working with dark mode, feel free to &lt;a href="https://web.dev/prefers-color-scheme/#activating-dark-mode-in-the-operating-system"&gt;skip the introduction&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Dark mode before Dark Mode
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DMQ-0ujp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/640/0%2AYZQl9JWZBeUzhdLF.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DMQ-0ujp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/640/0%2AYZQl9JWZBeUzhdLF.jpg" alt=""&gt;&lt;/a&gt;Green screen (&lt;a href="https://commons.wikimedia.org/wiki/File:Compaq_Portable_and_Wordperfect.JPG"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We have gone full circle with dark mode. In the dawn of personal computing, dark mode wasn’t a matter of choice, but a matter of fact: Monochrome CRT computer monitors worked by firing electron beams on a phosphorescent screen and the phosphor used in early CRTs was green. Because text was displayed in green and the rest of the screen was black, these models were often referred to as &lt;a href="https://commons.wikimedia.org/wiki/File:Schneider_CPC6128_with_green_monitor_GT65,_start_screen.jpg"&gt;green screens&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JynDJ8Z1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/698/0%2AILIBhfzAmuuP6fAK.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JynDJ8Z1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/698/0%2AILIBhfzAmuuP6fAK.jpg" alt=""&gt;&lt;/a&gt;Dark-on-white (&lt;a href="https://www.youtube.com/watch?v=qKkABzt0Zqg"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The subsequently introduced Color CRTs displayed multiple colors through the use of red, green, and blue phosphors. They created white by activating all three phosphors simultaneously. With the advent of more sophisticated WYSIWYG &lt;a href="https://en.wikipedia.org/wiki/Desktop_publishing"&gt;desktop publishing&lt;/a&gt;, the idea of making the virtual document resemble a physical sheet of paper became popular.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EA_UDgfO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AQOV2uc-yZ1Tef17O.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EA_UDgfO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AQOV2uc-yZ1Tef17O.png" alt=""&gt;&lt;/a&gt;The WorldWideWeb browser (&lt;a href="https://commons.wikimedia.org/wiki/File:WorldWideWeb_FSF_GNU.png"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This is where &lt;em&gt;dark-on-white&lt;/em&gt; as a design trend started, and this trend was carried over to the &lt;a href="http://info.cern.ch/hypertext/WWW/TheProject.html"&gt;early document-based web&lt;/a&gt;. The first ever browser, &lt;a href="https://en.wikipedia.org/wiki/WorldWideWeb"&gt;WorldWideWeb&lt;/a&gt; (remember, &lt;a href="https://en.wikipedia.org/wiki/Cascading_Style_Sheets#History"&gt;CSS wasn’t even invented&lt;/a&gt; yet), &lt;a href="https://commons.wikimedia.org/wiki/File:WorldWideWeb_FSF_GNU.png"&gt;displayed webpages&lt;/a&gt; this way. Fun fact: the second ever browser, &lt;a href="https://en.wikipedia.org/wiki/Line_Mode_Browser"&gt;Line Mode Browser&lt;/a&gt; — a terminal-based browser — was green on dark. These days, web pages and web apps are typically designed with dark text on a light background, a baseline assumption that is also hard-coded in user agent stylesheets, including &lt;a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/core/css/html.css"&gt;Chrome’s&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fImk0Jui--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/500/0%2A7glgTBgIbbQOgpju.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fImk0Jui--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/500/0%2A7glgTBgIbbQOgpju.jpg" alt=""&gt;&lt;/a&gt;Smartphone used in bed (&lt;a href="https://unsplash.com/photos/W39xsPWZgA4"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The days of CRTs are long over. Content consumption and creation has shifted to mobile devices that use backlit LCD or energy-saving AMOLED screens. Smaller and more transportable computers, tablets, and smartphones led to new usage patterns. Leisure tasks like web browsing, coding for fun, and high-end gaming frequently happen after-hours in dim environments. People even enjoy their devices in their beds at night-time. The more people use their devices in the dark, the more the idea of going back to the roots of &lt;em&gt;light-on-dark&lt;/em&gt; becomes popular.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why dark mode
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Dark mode for aesthetic reasons
&lt;/h4&gt;

&lt;p&gt;When people get asked &lt;a href="https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d"&gt;why they like or want dark mode&lt;/a&gt;, the most popular response is that &lt;em&gt;“it’s easier on the eyes,”_followed by _“it’s elegant and beautiful.”&lt;/em&gt; Apple in their &lt;a href="https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface"&gt;Dark Mode developer documentation&lt;/a&gt; explicitly writes: &lt;em&gt;“The choice of whether to enable a light or dark appearance is an aesthetic one for most users, and might not relate to ambient lighting conditions.”&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👩‍🔬 Read up more on &lt;a href="https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d"&gt;user research regarding why people want dark mode and how they use it&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NgXd-OC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/531/0%2AZfv5E4GlFzQp4ZLN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NgXd-OC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/531/0%2AZfv5E4GlFzQp4ZLN.png" alt=""&gt;&lt;/a&gt;System 7 CloseView (&lt;a href="https://archive.org/details/mac_Macintosh_System_7_at_your_Fingertips_1992"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;h4&gt;
  
  
  Dark mode as an accessibility tool
&lt;/h4&gt;

&lt;p&gt;There are also people who actually &lt;em&gt;need&lt;/em&gt; dark mode and use it as another accessibility tool, for example, users with low vision. The earliest occurrence of such an accessibility tool I could find is &lt;a href="https://en.wikipedia.org/wiki/System_7"&gt;System&lt;/a&gt;7’s &lt;em&gt;CloseView&lt;/em&gt; feature, which had a toggle for &lt;em&gt;Black on White&lt;/em&gt; and &lt;em&gt;White on Black&lt;/em&gt;. While System 7 supported color, the default user interface was still black-and-white.&lt;/p&gt;

&lt;p&gt;These inversion-based implementations demonstrated their weaknesses once color was introduced. User research by Szpiro &lt;em&gt;et al.&lt;/em&gt; on &lt;a href="https://dl.acm.org/citation.cfm?id=2982168"&gt;how people with low vision access computing devices&lt;/a&gt; showed that all interviewed users disliked inverted images, but that many preferred light text on a dark background. Apple accommodates for this user preference with a feature called &lt;a href="https://www.apple.com//accessibility/iphone/vision/"&gt;Smart Invert&lt;/a&gt;, which reverses the colors on the display, except for images, media, and some apps that use dark color styles.&lt;/p&gt;

&lt;p&gt;A special form of low vision is Computer Vision Syndrome, also known as Digital Eye Strain, which is &lt;a href="https://onlinelibrary.wiley.com/doi/full/10.1111/j.1475-1313.2011.00834.x"&gt;defined&lt;/a&gt; as &lt;em&gt;“the combination of eye and vision problems associated with the use of computers (including desktop, laptop, and tablets) and other electronic displays (e.g. smartphones and electronic reading devices).”&lt;/em&gt; It has been &lt;a href="https://bmjopen.bmj.com/content/5/1/e006748"&gt;proposed&lt;/a&gt; that the use of electronic devices by adolescents, particularly at night time, leads to an increased risk of shorter sleep duration, longer sleep-onset latency, and increased sleep deficiency. Additionally, exposure to blue light has been widely &lt;a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4254760/"&gt;reported&lt;/a&gt; to be involved in the regulation of &lt;a href="https://en.wikipedia.org/wiki/Circadian_rhythm"&gt;circadian rhythm&lt;/a&gt; and the sleep cycle, and irregular light environments may lead to sleep deprivation, possibly affecting mood and task performance, according to &lt;a href="https://www.college-optometrists.org/oip-resource/computer-vision-syndrome--a-k-a--digital-eye-strain.html"&gt;research by Rosenfield&lt;/a&gt;. To limit these negative effects, reducing blue light by adjusting the display color temperature through features like iOS’ &lt;a href="https://support.apple.com/en-us/HT207570"&gt;Night Shift&lt;/a&gt; or Android’s &lt;a href="https://support.google.com/pixelphone/answer/7169926"&gt;Night Light&lt;/a&gt; can help, as well as avoiding bright lights or irregular lights in general through dark themes or dark modes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dark mode power savings on AMOLED screens
&lt;/h4&gt;

&lt;p&gt;Finally, dark mode is known to save a &lt;em&gt;lot&lt;/em&gt; of energy on AMOLED screens. Android case studies that focused on popular Google apps like YouTube have shown that the power savings can be up to 60%. The video below has more details on these case studies and the power savings per app.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/N_6sPd0Jd3g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Activating dark mode in the operating system
&lt;/h3&gt;

&lt;p&gt;Now that I have covered the background of why dark mode is such a big deal for many users, let’s review how you can support it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0w4FiTWe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/610/0%2AErOuqrV3HKXKLA3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0w4FiTWe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/610/0%2AErOuqrV3HKXKLA3w.png" alt=""&gt;&lt;/a&gt;Android Q dark theme settings&lt;/p&gt;

&lt;p&gt;Operating systems that support a dark mode or dark theme typically have an option to activate it somewhere in the settings. On macOS X, it’s in the system preference’s &lt;em&gt;General&lt;/em&gt; section and called &lt;em&gt;Appearance&lt;/em&gt; (&lt;a href="https://web.dev/prefers-color-scheme/macosx.png"&gt;screenshot&lt;/a&gt;), and on Windows 10, it’s in the &lt;em&gt;Colors&lt;/em&gt; section and called &lt;em&gt;Choose your color&lt;/em&gt; (&lt;a href="https://web.dev/prefers-color-scheme/windows10.png"&gt;screenshot&lt;/a&gt;). For Android Q, you can find it under &lt;em&gt;Display&lt;/em&gt; as a &lt;em&gt;Dark Theme&lt;/em&gt; toggle switch (&lt;a href="https://web.dev/prefers-color-scheme/android.png"&gt;screenshot&lt;/a&gt;), and on iOS 13, you can change the &lt;em&gt;Appearance&lt;/em&gt; in the &lt;em&gt;Display &amp;amp; Brightness&lt;/em&gt; section of the settings (&lt;a href="https://web.dev/prefers-color-scheme/ios.jpg"&gt;screenshot&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query
&lt;/h3&gt;

&lt;p&gt;One last bit of theory before I get going. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries"&gt;Media queries&lt;/a&gt; allow authors to test and query values or features of the user agent or display device, independent of the document being rendered. They are used in the CSS @media rule to conditionally apply styles to a document, and in various other contexts and languages, such as HTML and JavaScript. &lt;a href="https://drafts.csswg.org/mediaqueries-5/"&gt;Media Queries Level 5&lt;/a&gt; introduces so-called user preference media features, that is, a way for sites to detect the user's preferred way to display content.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☝️ An established user preference media feature is prefers-reduced-motion that lets you detect the desire for less motion on a page. I have &lt;a href="https://developers.google.com/web/updates/2019/03/prefers-reduced-motion"&gt;written about&lt;/a&gt;&lt;a href="https://developers.google.com/web/updates/2019/03/prefers-reduced-motion"&gt;prefers-reduced-motion&lt;/a&gt; before.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme"&gt;prefers-color-scheme&lt;/a&gt; media feature is used to detect if the user has requested the page to use a light or dark color theme. It works with the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;no-preference&lt;/code&gt;: Indicates that the user has made no preference known to the system. This keyword value evaluates as &lt;code&gt;false&lt;/code&gt; in the &lt;a href="https://drafts.csswg.org/mediaqueries-5/#boolean-context"&gt;boolean context&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;light&lt;/code&gt;: Indicates that the user has notified the system that they prefer a page that has a light theme (dark text on light background).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dark&lt;/code&gt;: Indicates that the user has notified the system that they prefer a page that has a dark theme (light text on dark background).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Supporting dark mode
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Finding out if dark mode is supported by the browser
&lt;/h3&gt;

&lt;p&gt;As dark mode is reported through a media query, you can easily check if the current browser supports dark mode by checking if the media query prefers-color-schemematches at all. Note how I don't include any value, but purely check if the media query alone matches.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🎉 Dark mode is supported&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At the time of writing, prefers-color-scheme is supported on both desktop and mobile (where available) by Chrome and Edge as of version 76, Firefox as of version 67, and Safari as of version 12.1 on macOS and as of version 13 on iOS. For all other browsers, you can check the &lt;a href="https://caniuse.com/#feat=prefers-color-scheme"&gt;Can I use support tables&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is a custom element &lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt;available that adds dark mode support to older browsers. I write about it &lt;a href="https://web.dev/prefers-color-scheme/#the-lessdark-mode-togglegreater-custom-element"&gt;further down in this article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Dark mode in practice
&lt;/h3&gt;

&lt;p&gt;Let’s finally see how supporting dark mode looks like in practice. Just like with the &lt;a href="https://en.wikipedia.org/wiki/Highlander_(film)"&gt;Highlander&lt;/a&gt;, with dark mode &lt;em&gt;there can be only one&lt;/em&gt;: dark or light, but never both! Why do I mention this? Because this fact should have an impact on the loading strategy. Please don’t force users to download CSS in the critical rendering path that is for a mode they don’t currently use. To optimize load speed, I have therefore split my CSS for the example app that shows the following recommendations in practice into three parts in order to &lt;a href="https://web.dev/defer-non-critical-css/"&gt;defer non-critical CSS&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;style.css&lt;/code&gt; that contains generic rules that are used universally on the site.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dark.css&lt;/code&gt; that contains only the rules needed for dark mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;light.css&lt;/code&gt; that contains only the rules needed for light mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Loading strategy
&lt;/h3&gt;

&lt;p&gt;The two latter ones, light.css and dark.css, are loaded conditionally with a &lt;code&gt;&amp;lt;link media&amp;gt;&lt;/code&gt; query. Initially, &lt;a href="https://caniuse.com/#feat=prefers-color-scheme"&gt;not all browsers will support&lt;/a&gt;&lt;a href="https://caniuse.com/#feat=prefers-color-scheme"&gt;prefers-color-scheme&lt;/a&gt;(detectable using the &lt;a href="https://web.dev/prefers-color-scheme/#finding-out-if-dark-mode-is-supported-by-the-browser"&gt;pattern above&lt;/a&gt;), which I deal with dynamically by loading the default light.css file via a conditionally inserted &lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt; element in a minuscule inline script (light is an arbitrary choice, I could also have made dark the default fallback experience). To avoid a &lt;a href="https://en.wikipedia.org/wiki/Flash_of_unstyled_content"&gt;flash of unstyled content&lt;/a&gt;, I hide the content of the page until light.css has loaded.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
 &lt;span class="c1"&gt;// If `prefers-color-scheme` is not supported, fall back to light mode.&lt;/span&gt;
 &lt;span class="c1"&gt;// In this case, light.css will be downloaded with `highest` priority.&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;media&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not all&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;link rel="stylesheet" href="/light.css" onload="document.documentElement.style.display = ``"&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!--
 Conditionally either load the light or the dark stylesheet. The matching file
 will be downloaded with `highest`, the non-matching file with `lowest`
 priority. If the browser doesn't support `prefers-color-scheme`, the media
 query is unknown and the files are downloaded with `lowest` priority (but
 above I already force `highest` priority for my default light experience).
--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/dark.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: dark)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/light.css"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: no-preference), (prefers-color-scheme: light)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- The main stylesheet --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/style.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Stylesheet architecture
&lt;/h3&gt;

&lt;p&gt;I make maximum use of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var"&gt;CSS variables&lt;/a&gt;, this allows my generic style.css to be, well, generic, and all the light or dark mode customization happens in the two other files dark.css and light.css. Below you can see an excerpt of the actual styles, but it should suffice to convey the overall idea. I declare two variables, -⁠-⁠color and -⁠-⁠background-color that essentially create a &lt;em&gt;dark-on-light&lt;/em&gt; and a &lt;em&gt;light-on-dark&lt;/em&gt; baseline theme.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* light.css: 👉 dark-on-light */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* dark.css: 👉 light-on-dark */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In my style.css, I then use these variables in the body { … } rule. As they are defined on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:root"&gt;:root CSS pseudo-class&lt;/a&gt;—a selector that in HTML represents the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element and is identical to the selector html, except that its specificity is higher—they cascade down, which serves me for declaring global CSS variables.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* style.css */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In the code sample above, you will probably have noticed a property &lt;a href="https://drafts.csswg.org/css-color-adjust-1/#propdef-color-scheme"&gt;color-scheme&lt;/a&gt; with the space-separated value light dark.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The color-scheme property is still &lt;a href="https://crbug.com/925935"&gt;in development&lt;/a&gt; and it might not work as advertised, full support in Chrome will come later this year.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This tells the browser which color themes my app supports and allows it to activate special variants of the user agent stylesheet, which is useful to, for example, let the browser render form fields with a dark background and light text, adjust the scrollbars, or to enable a theme-aware highlight color. The exact details of color-scheme are specified in &lt;a href="https://drafts.csswg.org/css-color-adjust-1/"&gt;CSS Color Adjustment Module Level 1&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🌒 Read up more on &lt;a href="https://medium.com/dev-channel/what-does-dark-modes-supported-color-schemes-actually-do-69c2eacdfa1d"&gt;what &lt;code&gt;color-scheme&lt;/code&gt; actually does&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything else is then just a matter of defining CSS variables for things that matter on my site. Semantically organizing styles helps a lot when working with dark mode. For example, rather than -⁠-⁠highlight-yellow, consider calling the variable -⁠-⁠accent-color, as "yellow" may actually not be yellow in dark mode or vice versa. Below is an example of some more variables that I use in my example.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* dark.css */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--link-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;212&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--main-headline-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;233&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--accent-background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;212&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* light.css */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--link-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;238&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--main-headline-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;192&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--accent-background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;238&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="py"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Full example
&lt;/h3&gt;

&lt;p&gt;In the following &lt;a href="https://dark-mode-baseline.glitch.me/"&gt;Glitch&lt;/a&gt; embed, you can see the complete example that puts the concepts from above into practice. Try toggling dark mode in your particular &lt;a href="https://web.dev/prefers-color-scheme/#activating-dark-mode-in-the-operating-system"&gt;operating system’s settings&lt;/a&gt; and see how the page reacts.&lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/dark-mode-baseline?previewSize=100&amp;amp;path=index.html" alt="dark-mode-baseline on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Loading impact
&lt;/h3&gt;

&lt;p&gt;When you play with this example, you can see why I load my dark.css and light.css via media queries. Try toggling dark mode and reload the page: the particular currently non-matching stylesheets are still loaded, but with the lowest priority, so that they never compete with resources that are needed by the site right now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;😲 Read up more on &lt;a href="https://blog.tomayac.com/2018/11/08/why-browsers-download-stylesheets-with-non-matching-media-queries-180513"&gt;why browsers download stylesheets with non-matching media queries&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Axrwmzji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Al9ceKHmGLlj7jmxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Axrwmzji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Al9ceKHmGLlj7jmxy.png" alt=""&gt;&lt;/a&gt;Site in light mode loads the dark mode CSS with lowest priority.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_59dZS6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A0AmWb_46uTpJ-jk-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_59dZS6S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A0AmWb_46uTpJ-jk-.png" alt=""&gt;&lt;/a&gt;Site in dark mode loads the light mode CSS with lowest priority.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jzfaDJY4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AfJt_2qBrh-Jr2xqF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jzfaDJY4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AfJt_2qBrh-Jr2xqF.png" alt=""&gt;&lt;/a&gt;Site in default light mode on a browser that doesn’t support prefers-color-scheme loads the dark mode CSS with lowest priority.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reacting on dark mode changes
&lt;/h3&gt;

&lt;p&gt;Like any other media query change, dark mode changes can be subscribed to via JavaScript. You can use this to, for example, dynamically change the &lt;a href="https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/#provide_great_icons_tiles"&gt;favicon&lt;/a&gt; of a page or change the &lt;a href="https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/#meta_theme_color_for_chrome_and_opera"&gt;&lt;code&gt;&amp;lt;meta name="theme-color"&amp;gt;&lt;/code&gt;&lt;/a&gt; that determines the color of the URL bar in Chrome. The &lt;a href="https://web.dev/prefers-color-scheme/#full-example"&gt;full example&lt;/a&gt; above shows this in action, in order to see the theme color and favicon changes, open the &lt;a href="https://dark-mode-baseline.glitch.me/"&gt;demo in a separate tab&lt;/a&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;darkModeMediaQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nx"&gt;darkModeMediaQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;darkModeOn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Dark mode is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;darkModeOn&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🌒 on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;☀️ off&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Dark mode best practices
&lt;/h3&gt;
&lt;h3&gt;
  
  
  Avoid pure white
&lt;/h3&gt;

&lt;p&gt;A small detail you may have noticed is that I don’t use pure white. Instead, to prevent glowing and bleeding against the surrounding dark content, I choose a slightly darker white. Something like rgb(250, 250, 250) works well.&lt;/p&gt;
&lt;h3&gt;
  
  
  Re-colorize and darken photographic images
&lt;/h3&gt;

&lt;p&gt;If you compare the two screenshots below, you will notice that not only the core theme has changed from &lt;em&gt;dark-on-light&lt;/em&gt; to &lt;em&gt;light-on-dark&lt;/em&gt;, but that also the hero image looks slightly different. My &lt;a href="https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b"&gt;user research&lt;/a&gt; has shown that the majority of the surveyed people prefer slightly less vibrant and brilliant images when dark mode is active. I refer to this as &lt;em&gt;re-colorization&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--smP1NKHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Awh4lG2vnLdTRcu5U.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--smP1NKHv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2Awh4lG2vnLdTRcu5U.png" alt=""&gt;&lt;/a&gt;Hero image slightly darkened in dark mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--03529tiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AwHfT_my7AgR5o09a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--03529tiO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AwHfT_my7AgR5o09a.png" alt=""&gt;&lt;/a&gt;Regular hero image in light mode.&lt;/p&gt;

&lt;p&gt;Re-colorization can be achieved through a CSS filter on my images. I use a CSS selector that matches all images that don’t have .svg in their URL, the idea being that I can give vector graphics (icons) a different re-colorization treatment than my images (photos), more about this in the &lt;a href="https://web.dev/prefers-color-scheme/#vector-graphics-and-icons"&gt;next paragraph&lt;/a&gt;. Note how I again use a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var"&gt;CSS variable&lt;/a&gt;, so I can later on flexibly change my filter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎨 Read up more on &lt;a href="https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b"&gt;user research regarding re-colorization preferences with dark mode&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As re-colorization is only needed in dark mode, that is, when dark.css is active, there are no corresponding rules in light.css.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* dark.css */&lt;/span&gt;
&lt;span class="nt"&gt;--image-filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;grayscale&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;50&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;

&lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;".svg"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--image-filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Customizing dark mode re-colorization intensities with JavaScript
&lt;/h4&gt;

&lt;p&gt;Not everyone is the same and people have different dark mode needs. By sticking to the re-colorization method described above, I can easily make the grayscale intensity a user preference that I can &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties#Values_in_JavaScript"&gt;change via JavaScript&lt;/a&gt;, and by setting a value of 0%, I can also disable re-colorization completely. Note that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/documentElement"&gt;document.documentElement&lt;/a&gt;provides a reference to the root element of the document, that is, the same element I can reference with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:root"&gt;:rootCSS pseudo-class&lt;/a&gt;.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;const&lt;/span&gt; &lt;span class="nt"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;'grayscale(70%)'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;document&lt;/span&gt;&lt;span class="nc"&gt;.documentElement.style.setProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'--image-filter'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Invert vector graphics and icons
&lt;/h3&gt;

&lt;p&gt;For vector graphics — that in my case are used as icons that I reference via &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements—I use a different re-colorization method. While &lt;a href="https://dl.acm.org/citation.cfm?id=2982168"&gt;research&lt;/a&gt; has shown that people don't like inversion for photos, it does work very well for most icons. Again I use CSS variables to determine the inversion amount in the regular and in the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:hover"&gt;:hover&lt;/a&gt; state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--epkkiF9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/744/0%2AtMg3mLZc7MdOoj8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--epkkiF9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/744/0%2AtMg3mLZc7MdOoj8c.png" alt=""&gt;&lt;/a&gt;Icons are inverted in dark mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5dUrXHVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/744/0%2A9vONLnkqYt3BiDx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5dUrXHVx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/744/0%2A9vONLnkqYt3BiDx4.png" alt=""&gt;&lt;/a&gt;Regular icons in light mode.&lt;/p&gt;

&lt;p&gt;Note how again I only invert icons in dark.css but not in light.css, and how :hover gets a different inversion intensity in the two cases to make the icon appear slightly darker or slightly brighter, dependent on the mode the user has selected.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* dark.css */&lt;/span&gt;
&lt;span class="nt"&gt;--icon-filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;invert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;
&lt;span class="nt"&gt;--icon-filter_hover&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;invert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;

&lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;".svg"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-filter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="c"&gt;/* light.css */&lt;/span&gt;
&lt;span class="nt"&gt;--icon-filter_hover&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;invert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;60&lt;/span&gt;&lt;span class="o"&gt;%);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* style.css */&lt;/span&gt;
&lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;src&lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="s1"&gt;".svg"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--icon-filter_hover&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;currentColor&lt;/code&gt; for inline SVGs
&lt;/h3&gt;

&lt;p&gt;For &lt;em&gt;inline&lt;/em&gt; SVG images, instead of &lt;a href="https://web.dev/prefers-color-scheme/#invert-vector-graphics-and-icons"&gt;using inversion filters&lt;/a&gt;, you can leverage the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#currentColor_keyword"&gt;currentColor&lt;/a&gt; CSS keyword that represents the value of an element's color property. This lets you use the color value on properties that do not receive it by default. Conveniently, if currentColor is used as the value of the SVG &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Fills_and_Strokes#Fill_and_Stroke_Attributes"&gt;fill or&lt;/a&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Fills_and_Strokes#Fill_and_Stroke_Attributes"&gt;strokeattributes&lt;/a&gt;, it instead takes its value from the inherited value of the color property. Even better: this also works for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/use"&gt;&lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;use href="…"&amp;gt;&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/a&gt;, so you can have separate resources and currentColor will still be applied in context. Please note that this only works for &lt;em&gt;inline&lt;/em&gt; or &lt;code&gt;&amp;lt;use href="…"&amp;gt;&lt;/code&gt; SVGs, but not SVGs that are referenced as the src of an image or somehow via CSS. You can see this applied in the demo below.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Some inline SVG --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
 &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 […]
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/dark-mode-currentcolor?previewSize=100&amp;amp;path=index.html" alt="dark-mode-currentcolor on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Smooth transitions between modes
&lt;/h3&gt;

&lt;p&gt;Switching from dark mode to light mode or vice versa can be smoothed thanks to the fact that both color and background-color are &lt;a href="https://www.quackit.com/css/css3/animations/animatable_properties/"&gt;animatable CSS properties&lt;/a&gt;. Creating the animation is as easy as declaring two transitions for the two properties. The example below illustrates the overall idea, you can experience it live in the&lt;a href="https://dark-mode-baseline.glitch.me/"&gt;demo&lt;/a&gt;.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="py"&gt;--duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="py"&gt;--timing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--background-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--timing&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;background-color&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--timing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Art direction with dark mode
&lt;/h3&gt;

&lt;p&gt;While for loading performance reasons in general I recommend to exclusively work with prefers-color-scheme in the media attribute of &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elements (rather than inline in stylesheets), there are situations where you actually may want to work with prefers-color-scheme directly inline in your HTML code. Art direction is such a situation. On the web, art direction deals with the overall visual appearance of a page and how it communicates visually, stimulates moods, contrasts features, and psychologically appeals to a target audience.&lt;/p&gt;

&lt;p&gt;With dark mode, it’s up to the judgment of the designer to decide what is the best image at a particular mode and whether &lt;a href="https://web.dev/prefers-color-scheme/#photographic-images"&gt;re-colorization of images&lt;/a&gt; is maybe &lt;em&gt;not&lt;/em&gt; good enough. If used with the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element, the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; of the image to be shown can be made dependent on the media attribute. In the example below, I show the Western hemisphere for dark mode, and the Eastern hemisphere for light mode or when no preference is given, defaulting to the Eastern hemisphere in all other cases. This is of course purely for illustrative purposes. Toggle dark mode on your device to see the difference.&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"western.webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: dark)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"eastern.webp"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(prefers-color-scheme: light), (prefers-color-scheme: no-preference)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 `&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"eastern.webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/dark-mode-picture?previewSize=100&amp;amp;path=index.html" alt="dark-mode-picture on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Dark mode, but add an opt-out
&lt;/h3&gt;

&lt;p&gt;As mentioned in the &lt;a href="https://web.dev/prefers-color-scheme/#why-dark-mode"&gt;why dark mode&lt;/a&gt; section above, dark mode is an aesthetic choice for most users. In consequence, some users may actually like to have their operating system UI in dark, but still prefer to see their webpages the way they are used to seeing them. A great pattern is to initially adhere to the signal the browser sends through prefers-color-scheme, but to then optionally allow users to override their system-level setting.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt; custom element
&lt;/h4&gt;

&lt;p&gt;You can of course create the code for this yourself, but you can also just use a ready-made custom element (web component) that I have created right for this purpose. It’s called &lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt; and it adds a toggle (dark mode: on/off) or a theme switcher (theme: light/dark) to your page that you can fully customize. The demo below shows the element in action (oh, and I have also 🤫 silently snuck it in all of the &lt;a href="https://dark-mode-baseline.glitch.me/"&gt;other&lt;/a&gt; &lt;a href="https://dark-mode-currentcolor.glitch.me/"&gt;examples&lt;/a&gt; &lt;a href="https://dark-mode-picture.glitch.me/"&gt;above&lt;/a&gt;).&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dark-mode-toggle&lt;/span&gt;
 &lt;span class="na"&gt;legend=&lt;/span&gt;&lt;span class="s"&gt;"Theme Switcher"&lt;/span&gt;
 &lt;span class="na"&gt;appearance=&lt;/span&gt;&lt;span class="s"&gt;"switch"&lt;/span&gt;
 &lt;span class="na"&gt;dark=&lt;/span&gt;&lt;span class="s"&gt;"Dark"&lt;/span&gt;
 &lt;span class="na"&gt;light=&lt;/span&gt;&lt;span class="s"&gt;"Light"&lt;/span&gt;
 &lt;span class="na"&gt;remember=&lt;/span&gt;&lt;span class="s"&gt;"Remember this"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/dark-mode-toggle&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U0FxTRGJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/140/0%2AuXh_I436B20vKFFc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U0FxTRGJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/140/0%2AuXh_I436B20vKFFc.png" alt=""&gt;&lt;/a&gt;&amp;lt;dark-mode-toggle&amp;gt; in light mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p77Kyrv4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/140/0%2A-YKdMX1aGAEMXKh5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p77Kyrv4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/140/0%2A-YKdMX1aGAEMXKh5.png" alt=""&gt;&lt;/a&gt;&amp;lt;dark-mode-toggle&amp;gt; in dark mode.&lt;/p&gt;

&lt;p&gt;Try clicking or tapping the dark mode controls in the upper right corner in the demo below. If you check the checkbox in the third and the fourth control, see how your mode selection is remembered even when you reload the page. This allows your visitors to keep their operating system in dark mode, but enjoy your site in light mode or vice versa.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qF2jUiUG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-6a5bca60a4ebf959a6df7f08217acd07ac2bc285164fae041eacb8a148b1bab9.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/GoogleChromeLabs"&gt;
        GoogleChromeLabs
      &lt;/a&gt; / &lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;
        dark-mode-toggle
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A custom element that allows you to easily put a Dark Mode 🌒 toggle or switch on your site:
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="instapaper_body md"&gt;
&lt;p&gt;&lt;a href="https://www.webcomponents.org/element/dark-mode-toggle" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/e92dc407af2fd15cebe3fb7449c112c0bdc05e63/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f776562636f6d706f6e656e74732e6f72672d7075626c69736865642d626c75652e737667" alt="Published on webcomponents.org"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt; Element&lt;/h1&gt;
&lt;p&gt;A custom element that allows you to easily put a &lt;em&gt;Dark Mode 🌒&lt;/em&gt; toggle
or switch on your site, so you can initially adhere to your users' preferences according to
&lt;a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme" rel="nofollow"&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;
but also allow them to (optionally permanently) override their system setting for just your site.&lt;/p&gt;
&lt;p&gt;📚 Read all(!) about dark mode in the related article
&lt;a href="https://web.dev/prefers-color-scheme/" rel="nofollow"&gt;Hello Darkness, My Old Friend&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Installation&lt;/h2&gt;
&lt;p&gt;From npm:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell"&gt;&lt;pre&gt;npm install --save dark-mode-toggle&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the browser:&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-smi"&gt;DarkModeToggle&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;https://cdn.pika.dev/dark-mode-toggle&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-smi"&gt;DarkModeToggle&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;https://unpkg.com/dark-mode-toggle&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt;;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
Usage&lt;/h2&gt;
&lt;p&gt;⚠️ The custom element assumes that you have organized your CSS in different files
that you load conditionally based on the &lt;strong&gt;&lt;code&gt;media&lt;/code&gt;&lt;/strong&gt; attribute in the stylesheet's
corresponding &lt;code&gt;link&lt;/code&gt; element. This is a great performance pattern
as you don't force people to download CSS that they don't need
based on their current theme preference, yet non-matching…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;Working with and supporting dark mode is fun and opens up new design avenues. For some of your visitors it can be the difference between not being able to handle your site and being a happy user. There are some pitfalls and careful testing is definitely required, but dark mode is definitely a great opportunity for you to show that you care about all of your users. The best practices mentioned in this post and helpers like the &lt;a href="https://github.com/GoogleChromeLabs/dark-mode-toggle"&gt;&lt;code&gt;&amp;lt;dark-mode-toggle&amp;gt;&lt;/code&gt;&lt;/a&gt; custom element should make you confident in your ability to create an amazing dark mode experience. &lt;a href="https://twitter.com/tomayac"&gt;Let me know on Twitter&lt;/a&gt;what you create and if this post was useful or also suggestions for improving it. Thanks for reading! 🌒&lt;/p&gt;

&lt;h3&gt;
  
  
  Related links
&lt;/h3&gt;

&lt;p&gt;Resources for the prefers-color-scheme media query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chromestatus.com/feature/5109758977638400"&gt;Chrome Platform Status page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://crbug.com/889087"&gt;Chromium bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme"&gt;Media Queries Level 5 spec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resources for the color-scheme meta tag and CSS property:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chromestatus.com/feature/5330651267989504"&gt;Chrome Platform Status page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://crbug.com/925935"&gt;Chromium bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drafts.csswg.org/css-color-adjust-1/"&gt;CSS Color Adjustment Module Level 1 spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/csswg-drafts/issues/3299"&gt;CSS WG GitHub Issue for the meta tag and the CSS property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/whatwg/html/issues/4504"&gt;HTML WHATWG GitHub Issue for the meta tag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;General dark mode links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://material.io/design/color/dark-theme.html"&gt;Material Design — Dark Theme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webkit.org/blog/8892/dark-mode-in-web-inspector/"&gt;Dark Mode in Web Inspector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webkit.org/blog/8840/dark-mode-support-in-webkit/"&gt;Dark Mode Support in WebKit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/design/human-interface-guidelines/macos/visual-design/dark-mode/"&gt;Apple Human Interface Guidelines — Dark Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Background research articles for this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/dev-channel/what-does-dark-modes-supported-color-schemes-actually-do-69c2eacdfa1d"&gt;What Does Dark Mode’s “supported-color-schemes” Actually Do? 🤔&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/dev-channel/let-there-be-darkness-maybe-9facd9c3023d"&gt;Let there be darkness! 🌚 Maybe…&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b"&gt;Re-Colorization for Dark Mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Acknowledgements
&lt;/h3&gt;

&lt;p&gt;The prefers-color-scheme media feature, the color-scheme CSS property, and the related meta tag are the implementation work of 👏 &lt;a href="https://twitter.com/runeli"&gt;Rune Lillesveen&lt;/a&gt;. Rune is also a co-editor of the &lt;a href="https://drafts.csswg.org/css-color-adjust-1/"&gt;CSS Color Adjustment Module Level 1&lt;/a&gt;spec. I would like to 🙏 thank &lt;a href="https://www.linkedin.com/in/lukasz-zbylut/"&gt;Lukasz Zbylut&lt;/a&gt;, &lt;a href="https://twitter.com/rowan_m"&gt;Rowan Merewood&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/chiragd/"&gt;Chirag Desai&lt;/a&gt;, and &lt;a href="https://twitter.com/rob_dodson"&gt;Rob Dodson&lt;/a&gt; for their thorough reviews of this article. The &lt;a href="https://web.dev/prefers-color-scheme/#loading-strategy"&gt;loading strategy&lt;/a&gt; is the brainchild of &lt;a href="https://twitter.com/jaffathecake"&gt;Jake Archibald&lt;/a&gt;. &lt;a href="https://twitter.com/ecbos_"&gt;Emilio Cobos Álvarez&lt;/a&gt; has pointed me to the correct prefers-color-schemedetection method. The tip with referenced SVGs and currentColor came from &lt;a href="https://twitter.com/xeenon"&gt;Timothy Hatcher&lt;/a&gt;. Finally, I am thankful to the many anonymous participants of the various user studies that have helped shape the recommendations in this article. Hero image by &lt;a href="https://unsplash.com/photos/kujXUuh1X0o"&gt;Nathan Anderson&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>preferscolorscheme</category>
      <category>darkmode</category>
      <category>darktheme</category>
      <category>css</category>
    </item>
    <item>
      <title>Why Browsers Download Stylesheets With Non-Matching Media Queries</title>
      <dc:creator>Thomas Steiner</dc:creator>
      <pubDate>Thu, 08 Nov 2018 17:24:41 +0000</pubDate>
      <link>https://dev.to/chromiumdev/why-browsers-download-stylesheets-with-non-matching-media-queries-4gcn</link>
      <guid>https://dev.to/chromiumdev/why-browsers-download-stylesheets-with-non-matching-media-queries-4gcn</guid>
      <description>&lt;p&gt;(Originally posted on &lt;a href="https://blog.tomayac.com/2018/11/08/why-browsers-download-stylesheets-with-non-matching-media-queries-180513" rel="noopener noreferrer"&gt;blog.tomayac.com/2018/11/08/why-browsers-download-stylesheets-with-non-matching-media-queries-180513&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;The other day, I read an article by &lt;a href="https://github.com/dgieselaar" rel="noopener noreferrer"&gt;Dario Gieselaar&lt;/a&gt; on &lt;a href="https://medium.com/zoover-engineering/optimizing-css-by-removing-unused-media-queries-80b5508c6de9" rel="noopener noreferrer"&gt;Optimizing CSS by removing unused media queries&lt;/a&gt;. One of the core ideas is that you can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media" rel="noopener noreferrer"&gt;&lt;code&gt;media&lt;/code&gt; attribute&lt;/a&gt; when including your stylesheets like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link href="print.css" rel="stylesheet" media="print"&amp;gt;
&amp;lt;link href="mobile.css" rel="stylesheet" media="screen and (max-width: 600px)"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the article, Dario links to &lt;a href="https://github.com/scottjehl" rel="noopener noreferrer"&gt;Scott Jehl&lt;/a&gt;'s &lt;a href="http://scottjehl.github.io/CSS-Download-Tests/" rel="noopener noreferrer"&gt;CSS Downloads by Media Query&lt;/a&gt; test suite where Scott shows how &lt;em&gt;browsers would still download stylesheets even if their media queries are non-matching&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://github.com/scottjehl/CSS-Download-Tests/issues/11#issue-378471829" rel="noopener noreferrer"&gt;pointed out&lt;/a&gt; that the priority of these downloads is &lt;code&gt;Lowest&lt;/code&gt;, so they're at least not competing with core resources on the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmqcf1iuqvc2giqdq1pyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmqcf1iuqvc2giqdq1pyl.png" alt="Waterfall diagram showing how stylesheets with non-matching media queries are downloaded at lowest priority"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;At first sight this still seemed suboptimal, and I thought that even if the priority is &lt;code&gt;Lowest&lt;/code&gt;, maybe the browser shouldn't trigger downloads at all. So I did some research, and, surprise, it turns out that the CSS spec writers and browser implementors are actually pretty darn smart about this:&lt;/p&gt;

&lt;p&gt;The thing is, the user &lt;em&gt;could&lt;/em&gt; always decide to resize their window (impacting &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/width" rel="noopener noreferrer"&gt;width&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/height" rel="noopener noreferrer"&gt;height&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/aspect-ratio" rel="noopener noreferrer"&gt;aspect ratio&lt;/a&gt;), to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Media_types" rel="noopener noreferrer"&gt;print&lt;/a&gt; the document, &lt;em&gt;etc.&lt;/em&gt;, and even things that at first sight &lt;em&gt;seem&lt;/em&gt; static (like the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/resolution" rel="noopener noreferrer"&gt;resolution&lt;/a&gt;) can change when a user with a multi-screen setup moves a window from say a &lt;em&gt;Retina&lt;/em&gt; laptop screen to a bigger desktop monitor, or the user can unplug their &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer" rel="noopener noreferrer"&gt;mouse&lt;/a&gt;, and so on.&lt;/p&gt;

&lt;p&gt;Truly static things that can't change (a TV device can't suddenly turn into something else) are actually being &lt;em&gt;deprecated&lt;/em&gt; in &lt;a href="https://drafts.csswg.org/mediaqueries" rel="noopener noreferrer"&gt;Media Queries Level 4&lt;/a&gt; (see the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#Media_types" rel="noopener noreferrer"&gt;yellow note box&lt;/a&gt;); and the recommendation is to rather target &lt;a href="https://drafts.csswg.org/mediaqueries/#media-feature" rel="noopener noreferrer"&gt;media features&lt;/a&gt; instead (see the text under the &lt;a href="https://drafts.csswg.org/mediaqueries/#media-types" rel="noopener noreferrer"&gt;red issue box&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Finally, even invalid values like &lt;code&gt;media="nonsense"&lt;/code&gt; still need to be considered, according to the &lt;a href="https://www.w3.org/TR/CSS2/conform.html#ignore" rel="noopener noreferrer"&gt;ignore rules&lt;/a&gt; in the spec.&lt;/p&gt;

&lt;p&gt;So long story short, browsers try to be as smart as possible by applying priorities, and &lt;code&gt;Lowest&lt;/code&gt; is a reasonable value for the cases in Scott's &lt;a href="http://scottjehl.github.io/CSS-Download-Tests/" rel="noopener noreferrer"&gt;test&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mediaqueries</category>
      <category>css</category>
      <category>loading</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
