<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Polypane Blog]]></title><description><![CDATA[Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps, with multi-viewport synchronised previews and accessibility tooling.]]></description><link>https://polypane.app</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 14 Sep 2026 15:30:41 GMT</lastBuildDate><item><title><![CDATA[Polypane 30.1: Selector specificity insights, server HTML validation, and more]]></title><description><![CDATA[Polypane 30.1 adds selector specificity highlighting to the elements panel, a new server-sent HTML view in the sources panel, passkey…]]></description><link>https://polypane.app/blog/polypane-30-1-selector-specificity-insights-server-html-validation-and-more/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-30-1-selector-specificity-insights-server-html-validation-and-more/</guid><pubDate>Fri, 28 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 30.1 adds selector specificity highlighting to the elements panel, a new server-sent HTML view in the sources panel, passkey support on Linux and a bunch of improvements and fixes. Like Polypane 30 this release is built on Chromium 152.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;selector-specificity-insights&quot;&gt;Selector specificity insights&lt;/h2&gt;&lt;p&gt;Back in 2020 we launched Polypane&amp;#x27;s Elements panel, which pioneered showing the selector specificity of your styles right in the elements panel. Not much later we added an online &lt;a href=&quot;/css-specificity-calculator/&quot;&gt;specificity calculator&lt;/a&gt; that you could open from the Elements panel to explain selectors.&lt;/p&gt;&lt;p&gt;In Polypane 30.1, we&amp;#x27;re taking this a step further.&lt;/p&gt;&lt;p&gt;When you hover over a selector in the elements panel, Polypane will now highlight the parts of the selector that contribute to its specificity. It works for plain selectors:&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/specificity1-a20b1665677c1ea213adefeca98ee0c5.png&quot; alt=&quot;Selector specificity insights&quot;/&gt;&lt;p&gt;and for nested selectors as well:&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/specificity2-b1ef8bcc7e02813b55653365d042b262.png&quot; alt=&quot;Selector specificity insights&quot;/&gt;&lt;blockquote&gt;&lt;p&gt;When you nest CSS, Polypane shows the resolved selector for the nested rule in the tooltip so you know what the engine is doing under the hood. And now this is of course also highlighted!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;For nested CSS selectors, you can see that not only does it highlight the parent selector values, but is also intelligent enough to know that &lt;code class=&quot;language-text&quot;&gt;.card, #highspecificity&lt;/code&gt; counts as a single ID selector, even if &lt;code class=&quot;language-text&quot;&gt;#highspecificity&lt;/code&gt; is not part of the DOM.&lt;/p&gt;&lt;p&gt;Lastly, If you have a complex selector with lots of nesting, Polypane&amp;#x27;s highlighting will drill down to highlight the pertinent parts, rather than listing the single top-level wrapped &lt;code class=&quot;language-text&quot;&gt;:is()&lt;/code&gt; as being one value:&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/specificity3-c54d09c54df2b31c838e6ce74ed85c41.png&quot; alt=&quot;Selector specificity insights&quot;/&gt;&lt;p&gt;Drilling down into the selector required us to rebuild our specificity calculation engine, but the end result is an even more robust, accurate and understandable specificity.&lt;/p&gt;&lt;p&gt;We made all of these improvements available in our free online &lt;a href=&quot;/css-specificity-calculator/&quot;&gt;specificity calculator&lt;/a&gt; as well, so check it out!&lt;/p&gt;&lt;h2 id=&quot;gap-values-in-peek-tooltip&quot;&gt;Gap values in Peek tooltip&lt;/h2&gt;&lt;p&gt;When you use the Peek tool to inspect an element, Polypane will now show you the gap values in the tooltip. Just like the Padding and Margin values, this lets you quickly check the gap values without needing to go to the Elements panel and finding the right declaration.&lt;/p&gt;&lt;h2 id=&quot;passkeys-support-on-linux&quot;&gt;Passkeys support on Linux&lt;/h2&gt;&lt;p&gt;Polypane has had support for passkeys/webauthn on Windows and Mac, which both have operating-system backends to support them, and now Linux support has been added as well. The Linux implementation uses software passkeys through 1Password&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;passkey-rs&lt;/code&gt; library.&lt;/p&gt;&lt;p&gt;Because the Linux implementation does not rely on a platform backend it comes with a custom UI:&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/passkey-f6c5370d1d69f8bec335c58fc9a22d74.png&quot; alt=&quot;Passkey support on Linux&quot;/&gt;&lt;p&gt;This brings parity between the three platforms when it comes to passkey support, and allows you to test your passkey implementations on all three platforms!&lt;/p&gt;&lt;h2 id=&quot;server-sent-html-view-in-the-sources-panel&quot;&gt;Server-sent HTML view in the sources panel&lt;/h2&gt;&lt;p&gt;The sources panel now has a new toggle to switch between the rendered HTML (after your JS has run) and the plain server-sent HTML (before your JS has run). This is useful for debugging server-side rendering issues, and for checking that your server is sending the correct HTML to the browser.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/sourcepanel-d5ac4a99092d9eb18848b04b68fc4fe2.png&quot; alt=&quot;Server-sent HTML view in the sources panel&quot;/&gt;&lt;p&gt;Both sets of HTML are validated individually, so you can also easily see where HTML validation errors are being introduced.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Matija&lt;/strong&gt;  for asking about this feature!!&lt;/aside&gt;&lt;h2 id=&quot;updated-placeholdifier-debug-overlay&quot;&gt;Updated placeholdifier debug overlay&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve updated our placeholdifier debug overlay to use a scribble-style font instead of the block font we used before. This makes the blocked content more &amp;quot;text-like&amp;quot; and makes it easier to focus on the design instead of the content.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/placeholdifier-53298fcd6983aa64e1e65876faf6e1e8.png&quot; alt=&quot;Placeholdifier debug overlay&quot;/&gt;&lt;h2 id=&quot;chromium-152&quot;&gt;Chromium 152&lt;/h2&gt;&lt;p&gt;Polypane 30.1 is built on Chromium 152. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-301&quot;&gt;Get Polypane 30.1&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac, and Linux in 64-bit and ARM versions. Polypane automatically updates, but you can also go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14-day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-30-changelog&quot;&gt;Polypane 30 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Highlight specificity parts in selector&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Source panel: Toggle between rendered and server-sent HTML (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Passkey/Webauthn support for Linux&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Updated Chromium&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improved&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessibility panel now also checks for WCAG 2.2 2.5.8 target size&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Show a warning for multiple viewport declarations (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: improved header order design and updated ruleset&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: After inspecting an element the DOM tree is now focused&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Peek: show gap values in tooltip&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug tools: Placeholdifier now uses a scribble instead of a block font&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Find in page has improved performance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Basic auth popups are now shown in the browse panel&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panel: focuses the first ordered panel on launch&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Copy image to clipboard works again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Domain custom color was in wrong position in split views&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Detached network panel shows data again (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Detached panel window shows overflow menu again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Misaligned layout debugging badge for html element&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: specificity explainer link works again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Devtools panel: Issue on linux where the panel would not show content&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Recording: pages without an explicit background color now record on white instead of black&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Peek: decouple showing font-weight from showing line-height value&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Peek: prevent peek from showing when inspection mode is chrome devtools&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: Shortcuts now work in detached panel too&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Polypane 30: Playgrounds, Side Panel extension API, updated video recording and Chromium 152]]></title><description><![CDATA[Polypane 30 introduces Playgrounds so you can open directories as real HTTPS URLs in seconds, adds support for the Side Panel extension API…]]></description><link>https://polypane.app/blog/polypane-30-playgrounds-side-panel-extension-api-updated-video-recording-and-chromium-152/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-30-playgrounds-side-panel-extension-api-updated-video-recording-and-chromium-152/</guid><pubDate>Wed, 19 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 30 introduces Playgrounds so you can open directories as real HTTPS URLs in seconds, adds support for the Side Panel extension API, brings major Network panel improvements, and includes a fully rewritten video recording implementation that&amp;#x27;s much faster and supports recording sound. It runs on Chromium 152.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;polypane-playgrounds&quot;&gt;Polypane Playgrounds&lt;/h2&gt;&lt;p&gt;Playgrounds lets you select any directory on your device and open it as a real URL over HTTPS. That gives you access to security-restricted APIs, cookies, and localStorage.&lt;/p&gt;&lt;p&gt;That way you can test your work without all the fuss of setting up a local server, managing ports and dealing with self-signed certificates.&lt;/p&gt;&lt;p&gt;You choose a folder and a localhost domain, and Polypane does all the work.&lt;/p&gt;&lt;p&gt;Setting up a Playground is a matter of opening the Playgrounds Manager (through right-clicking the reload button, finding it in the Edit menu, or typing &amp;quot;playground&amp;quot; in our &lt;a href=&quot;/docs/command-bar&quot;&gt;command bar&lt;/a&gt;) and adding a playground:&lt;/p&gt;&lt;img src=&quot;static/playgroundsmanager-91a035c69e502b6d8a64afdae9e49d1b.png&quot; alt=&quot;The playgrounds manager&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;But Playgrounds is much more than just a slightly-more-convenient http-server, and it comes with a bunch of developer goodness:&lt;/p&gt;&lt;h3 id=&quot;live-reloading&quot;&gt;Live reloading&lt;/h3&gt;&lt;p&gt;By default, Playground URLs hook into Polypane&amp;#x27;s &lt;a href=&quot;/docs/live-auto-reloading/&quot;&gt;live reloading system&lt;/a&gt;. Any change you make to a file in the playground will trigger an update in Polypane.&lt;/p&gt;&lt;p&gt;Polypane re-injects CSS and images without a full reload, ignores system files, and reloads HTML when it changes. Just open your HTML file in any code editor and every save will be visible in Polypane.&lt;/p&gt;&lt;h3 id=&quot;spa-fallback&quot;&gt;SPA Fallback&lt;/h3&gt;&lt;p&gt;Are you trying out an SPA or using client-side routing? Playgrounds can be configured to always serve your index.html for unknown URLs.&lt;/p&gt;&lt;p&gt;That way your SPA can use client-side routing like the history and navigation API without configuring any server!&lt;/p&gt;&lt;h3 id=&quot;directory-listings&quot;&gt;Directory listings&lt;/h3&gt;&lt;p&gt;Open any file directory (without an &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt;) to show a listing of the files in that directory. Navigate into sub-directories and show or hide hidden files. The directory listing is fully styled and supports dark mode, so it works well in any pane. This works for Playgrounds and the file protocol.&lt;/p&gt;&lt;img src=&quot;static/directorylisting-07ad45a2105f34f3da3bf53b4c82c621.png&quot; alt=&quot;The directory listing for a playground&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;If you don&amp;#x27;t want directory listings for a particular Playground, you can disable it in the playground settings.&lt;/p&gt;&lt;h3 id=&quot;portal-enabled&quot;&gt;Portal-enabled&lt;/h3&gt;&lt;p&gt;Lastly, Playgrounds work perfectly together with &lt;a href=&quot;/docs/polypane-portal/&quot;&gt;Polypane Portal&lt;/a&gt; to make your playground available on other browsers and devices, and have them all synced together.&lt;/p&gt;&lt;p&gt;So you can set a folder up as a Playground, open it in Polypane, and then share it with your team or clients. They can open the same Playground on their devices and see the same content, with synced scroll, hover and focus states.&lt;/p&gt;&lt;video src=&quot;static/portal-28adc198ae7b9bd8021f6ec47115ed79.mp4&quot; alt=&quot;Polypane Portal in action&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;p&gt;I don&amp;#x27;t think there&amp;#x27;s any faster way to go from an &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; on your device to a fully synced, multi-device test environment that&amp;#x27;s available worldwide.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Credit where credit is due&lt;/strong&gt; The only reason Playgrounds exists is because of the Firefox Devtools team creating &lt;a href=&quot;https://firefox-source-docs.mozilla.org/devtools-user/local_mode/index.html&quot;&gt;Local mode&lt;/a&gt; for Firefox 153. Go try it out! They came up with the concept, I just took it and gave it a Polypane spin.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;network-panel-improvements&quot;&gt;Network panel improvements&lt;/h2&gt;&lt;p&gt;Thanks to everyone who wrote in to share their experiences with the new Network panel. We fixed several bugs (thanks Matija!) and added a couple of new features to make it even better.&lt;/p&gt;&lt;h3 id=&quot;toggle-between-per-origin-and-all-requests&quot;&gt;Toggle between per origin and all requests&lt;/h3&gt;&lt;p&gt;The Network panel splits requests per origin, a design decision that helps you get a better overview of the requests made by a page and easily see how many external origins are involved in loading it.&lt;/p&gt;&lt;p&gt;Having the full list of requests is still useful because it can tell you when those external requests are made during load, and what the full request tree looks like. So you can now toggle between the two views with a new button in the top right of the Network panel.&lt;/p&gt;&lt;img src=&quot;static/filter-7af513b0ec04949a53557bce0a92321c.png&quot; class=&quot;imgshadow&quot; alt=&quot;Network filter&quot;/&gt;&lt;h3 id=&quot;filter-requests&quot;&gt;Filter requests&lt;/h3&gt;&lt;p&gt;You can now filter requests by typing in the filter input. The filter checks against the URL and its headers. Filtering is case-insensitive by default, but also supports case-sensitive and regex filtering.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/filter-7af513b0ec04949a53557bce0a92321c.png&quot; alt=&quot;Filtering requests in the network panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;Testing against headers lets you do things like find every request served by Cloudflare.&lt;/p&gt;&lt;h3 id=&quot;highlighting-initiators&quot;&gt;Highlighting initiators&lt;/h3&gt;&lt;p&gt;When you hover over an initiator, the panel now highlights the request that initiated it. This makes it much easier to see which request caused a specific request to be made, and lets you very quickly drill down your request tree.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Matija&lt;/strong&gt;  for asking about this feature!!&lt;/aside&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/initiator-a22da7ea6dbebba52de951f9f35824fd.png&quot; alt=&quot;The initiator of a request is highlighted when hovering over it&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;The initiator in each request line has a tooltip that also shows which file or function initiated this request. Now you no longer have to find the request that initiated it in the list, as it&amp;#x27;s highlighted for you.&lt;/p&gt;&lt;h2 id=&quot;video-recording-improvements&quot;&gt;Video recording improvements&lt;/h2&gt;&lt;p&gt;The previous video recording implementation had a couple of things I wasn&amp;#x27;t happy with. Recording a video took a lot of memory (so you couldn&amp;#x27;t make long videos), processing the video after creation was slow and it didn&amp;#x27;t support sound.&lt;/p&gt;&lt;p&gt;Polypane 30 has a fully rewritten video recording implementation that processes videos essentially instantly and because of that has a much lower memory footprint. Videos are created as &lt;code class=&quot;language-text&quot;&gt;webm&lt;/code&gt; and include any sound that was playing in the pane.&lt;/p&gt;&lt;video src=&quot;static/video1-e36e7ac08a2d344a5f25e9575f39a90c.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;Everything else remains the same: you record a video, Polypane highlights clicks and you can trim and annotate the video with our built-in video editor (made with the amazing &lt;a href=&quot;https://pqina.nl/pintura?atp=lsxmJ1&quot;&gt;Pintura&lt;/a&gt;).&lt;/p&gt;&lt;h2 id=&quot;extension-side-panel-api&quot;&gt;Extension side panel API&lt;/h2&gt;&lt;p&gt;Polypane&amp;#x27;s extension API support has been extended with the Side Panel API. Extensions like &lt;a href=&quot;https://chromewebstore.google.com/detail/look-up-wcag-scs-wcag-suc/koppadkncigccambhpcmgilnphmaahad&quot;&gt;Look up WCAG SCs&lt;/a&gt; by Ian Lloyd (or those AI sidebar tools) can now add a sidebar to Polypane.&lt;/p&gt;&lt;img src=&quot;static/sidepanel-9beeb3d8b19ba79c630eb7eff796da0e.png&quot; alt=&quot;Side panel API&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;/&gt;&lt;p&gt;The sidebar lives in the Panel, and will automatically be added or removed depending on the extension state.&lt;/p&gt;&lt;h2 id=&quot;extension-tabcapture-apis&quot;&gt;Extension tabCapture APIs&lt;/h2&gt;&lt;p&gt;Extensions using the various tabCapture APIs to create screenshots or videos are now also fully supported.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Kathryn&lt;/strong&gt; for reporting the issue!&lt;/aside&gt;&lt;h3 id=&quot;other-extension-improvements&quot;&gt;Other extension improvements&lt;/h3&gt;&lt;p&gt;As users report issues with specific extensions, I have been working to make sure Polypane supports those extensions better by fixing gaps in our APIs or adding support for more APIs.&lt;/p&gt;&lt;p&gt;For this release, I made sure that the APIs used by the &lt;a href=&quot;https://keepassxc.org/&quot;&gt;KeePassXC&lt;/a&gt; extension work correctly in Polypane.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Mateus&lt;/strong&gt; for reporting the issue!&lt;/aside&gt;&lt;p&gt;Extensions using the &lt;code class=&quot;language-text&quot;&gt;browser&lt;/code&gt; namespace instead of &lt;code class=&quot;language-text&quot;&gt;chrome&lt;/code&gt; now have better support, string replacement has broader support, and managing popups and extension permissions now cover more cases.&lt;/p&gt;&lt;p&gt;Polypane now automatically opens &lt;code class=&quot;language-text&quot;&gt;chrome-extension://&lt;/code&gt; links in full layout, so you can use the extension&amp;#x27;s UI as intended.&lt;/p&gt;&lt;h2 id=&quot;elements-panel-improvements&quot;&gt;Elements panel improvements&lt;/h2&gt;&lt;p&gt;The elements panel got a few upgrades in Polypane 30.&lt;/p&gt;&lt;h3 id=&quot;quirks-mode-badge&quot;&gt;Quirks mode badge&lt;/h3&gt;&lt;p&gt;When a page is running in quirks mode (when you don&amp;#x27;t have a doctype) the Elements panel now shows a warning badge where normally the doctype is.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/quirksmode-e0c3fd544294544b803a97fdfb660d4a.png&quot; alt=&quot;The quirks mode badge in the elements panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;Quirks mode has a lot of weird implications for how a page is rendered, but the absence of a doctype isn&amp;#x27;t the clearest indicator of that. The badge now makes it clear that the page is running in quirks mode.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Roma&lt;/strong&gt; for suggesting this!&lt;/aside&gt;&lt;p&gt;&lt;em&gt;This suggestion happened in a Mastodon conversation between Nicolas of the Firefox Devtools team, Roma and me, and it&amp;#x27;s also coming to Firefox: &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=2040627&quot;&gt; Bug 2040627 &lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;h3 id=&quot;support-for-commandforelement-in-the-dom-tree&quot;&gt;Support for &lt;code class=&quot;language-text&quot;&gt;commandForElement&lt;/code&gt; in the DOM tree&lt;/h3&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;command&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;commandfor&lt;/code&gt; attributes of the invoker API make it easy to create custom interactions without needing JavaScript. But &lt;code class=&quot;language-text&quot;&gt;commandfor&lt;/code&gt; needs to reference another element by ID, and adding IDs all over the place (that need to be unique) can be bothersome.&lt;/p&gt;&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement/commandForElement&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;commandForElement&lt;/code&gt; API&lt;/a&gt; lets you reference any element directly with JavaScript, so no ID is needed. That&amp;#x27;s really useful, but it creates a challenge for the DOM tree, because the value of &lt;code class=&quot;language-text&quot;&gt;commandForElement&lt;/code&gt; isn&amp;#x27;t a string but a direct reference.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/commandforelement-5ca319e892350b07bc41f9a2c74f587c.png&quot; alt=&quot;The commandForElement badge in the elements panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;The Elements panel now supports this by showing the referenced element as a badge in the DOM tree that you can click to jump to the referenced element.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Rik&lt;/strong&gt; (of &lt;a href=&quot;/pintura?atp=lsxmJ1&quot;&gt;Pintura&lt;/a&gt; fame!) for reporting on this!&lt;/aside&gt;&lt;h3 id=&quot;support-for-if-in-css-properties-with-proper-indentation&quot;&gt;Support for &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; in CSS properties with proper indentation&lt;/h3&gt;&lt;p&gt;The new &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; CSS function broke our CSS parsing logic, causing the elements panel to look broken. We now support &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; in CSS properties and even preserve the indentation you&amp;#x27;re using in your CSS, so things stay readable in the way you prefer.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/ifsupport-1b0087d65738d8997eb1779d9d29492d.png&quot; alt=&quot;A style using if() in the elements panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;h3 id=&quot;selectable-pseudos-in-the-dom-tree&quot;&gt;Selectable pseudos in the DOM tree&lt;/h3&gt;&lt;p&gt;Pseudo elements like &lt;code class=&quot;language-text&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;::after&lt;/code&gt; are not part of the DOM so in the Polypane elements panel, they were not individually selectable. Editing their styles happens through their parent element.&lt;/p&gt;&lt;p&gt;People expect to be able to click on these pseudo elements though, so in Polypane 30 you can click them and it automatically opens the styles for that pseudo element in the Styles overview.&lt;/p&gt;&lt;h3 id=&quot;expanding-text-editor&quot;&gt;Expanding text editor&lt;/h3&gt;&lt;p&gt;The text and HTML editors in the navigator are one of our favorite features, because they make editing the text or HTML of an element really easy without any of the focus or modal requirements and, in the case of the text editor, live feedback.&lt;/p&gt;&lt;p&gt;At our CSS Day booth, Bernd asked if we could make the text editor resizable so that it&amp;#x27;s easier to work with larger amounts of text, and we thought that was a great idea. The text editor now automatically scales to the content.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Bernd&lt;/strong&gt; for requesting this feature at CSS Day!&lt;/aside&gt;&lt;h3 id=&quot;accessibility-property-tooltips&quot;&gt;Accessibility property tooltips&lt;/h3&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/a11ytooltip-17a7f98f333895524f4991050c9faecd.png&quot; alt=&quot;A tooltip showing for the &amp;#x27;controls&amp;#x27; property explaining that it&amp;#x27;s a list of IDs controlled by this element&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;If you look at the accessibility overview and are unsure what a specific property means, you can hover over it to see a description of what it is and when it is relevant.&lt;/p&gt;&lt;h2 id=&quot;default-inspection-tools&quot;&gt;Default inspection tools&lt;/h2&gt;&lt;p&gt;The devtools icons, inspect buttons, shortcuts, and context menus now always open the same devtools: Polypane&amp;#x27;s Elements panel or Chromium DevTools. This lowers the mental load of remembering which devtools you opened last, and makes it easier to use devtools in Polypane.&lt;/p&gt;&lt;img src=&quot;static/pickdevtools-6a0a597a66b72bd53d678f664a06d624.png&quot; class=&quot;imgshadow&quot; alt=&quot;Pick default devtools&quot; style=&quot;max-width:300px&quot;/&gt;&lt;p&gt;Right-click the devtools button in the header to set a default devtools, and all devtools buttons and shortcuts will open that devtools.&lt;/p&gt;&lt;p&gt;Both devtools are still available in the panel, so you can default to Polypane DevTools and open Chromium DevTools when you need, for example, React DevTools.&lt;/p&gt;&lt;p&gt;To learn more about the differences between the Polypane and Chromium devtools, check out docs on &lt;a href=&quot;/docs/developer-tools/&quot;&gt;Chromium &amp;amp; Polypane devtools&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;peek-improvements&quot;&gt;Peek improvements&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;/docs/polypane-peek/&quot;&gt;Polypane peek&lt;/a&gt; lets you hold &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌥&lt;/kbd&gt; to inspect elements. It&amp;#x27;s really, &lt;em&gt;really&lt;/em&gt; nice.&lt;/p&gt;&lt;img src=&quot;/doc-img/elementspanel/inspect.png&quot; class=&quot;imgshadow&quot; alt=&quot;Inspect overlay and tooltip.&quot;/&gt;&lt;p&gt;This release has a few changes to make it better:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Touch emulation is disabled when using peek so hovering works (Thanks Marc!)&lt;/li&gt;&lt;li&gt;Scroll syncing is disabled when using Peek, so pages no longer jump when the tooltip causes a scroll event to be sent to the other panes&lt;/li&gt;&lt;li&gt;Our color preview now supports the &lt;code class=&quot;language-text&quot;&gt;color-mix()&lt;/code&gt; function to the previews display those correctly (Thanks Marc!). We&amp;#x27;ll be updating color support throughout the app in upcoming releases.&lt;/li&gt;&lt;li&gt;If an element has specific aria attributes, Peek now shows those in the tooltip as well (Thanks Eric!)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;emulation-improvements&quot;&gt;Emulation improvements&lt;/h2&gt;&lt;p&gt;There are new emulation features in Polypane 30, and some existing ones have been improved.&lt;/p&gt;&lt;h3 id=&quot;text-scale-emulation&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt; emulation&lt;/h3&gt;&lt;p&gt;Browsers, by default, ignore OS-level text scaling, as many sites are not built to handle a default font size other than &lt;code class=&quot;language-text&quot;&gt;16px&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;There is a new &lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt; meta tag that will let you tell the browser how to respond to the OS-level text scaling. It looks like this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- The default, old behavior --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-scale&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;legacy&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Opting in to the new behavior --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text-scale&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;scale&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By setting the text-scale meta tag to &lt;code class=&quot;language-text&quot;&gt;scale&lt;/code&gt;, you opt in to using the OS-level text scaling. So if a user has e.g. a 200% scale on their Android Phone, your site&amp;#x27;s default font size is now 32px instead of 16px. This is a great way to make your site more accessible to users that need larger text.&lt;/p&gt;&lt;p&gt;In the emulations panel you can now easily test this by changing the OS text scale from the default of 100% to between 85 and 200%.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/textscale-96d31417af89163cf04fd1eb84fbe8a6.png&quot; alt=&quot;The text-scale emulation in the emulations panel&quot; style=&quot;max-width:50%&quot;/&gt;&lt;p&gt;With text-scale set to legacy, this will have no effect, but with text-scale set to scale, you can see how your site responds to the OS-level text scaling.&lt;/p&gt;&lt;h3 id=&quot;svh-difference-overlay&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; difference overlay&lt;/h3&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; difference was always applied, but the visual overlay was off by default. This led to confusion for people who didn&amp;#x27;t realize their sites or themes used &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt;, and instead reported &amp;quot;blank areas&amp;quot;.&lt;/p&gt;&lt;p&gt;To prevent this confusion, Polypane 30 splits out the &lt;code class=&quot;language-text&quot;&gt;safe-area-insets&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; overlay displays and turns the svh overlay on for all devices that support it.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/svh-109c6feffe8ed293f208e783211df992.png&quot; alt=&quot;The svh difference overlay&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;The overlay now has a new style to make it clear that the overlay is showing the difference between &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;vh&lt;/code&gt;, and has an info icon to explain it.&lt;/p&gt;&lt;p&gt;You can toggle the overlay through the media and emulations popup for a pane.&lt;/p&gt;&lt;h3 id=&quot;new-devices&quot;&gt;New devices&lt;/h3&gt;&lt;p&gt;The new iPhone 17e and Pixel 10 devices are now available in Polypane. Both are fully emulated, &lt;strong&gt;including safe-area-insets and &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; emulation&lt;/strong&gt;, only available in Polypane.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/new30devices-515a003f82ff1e1979fdfbf5e7354934.png&quot; alt=&quot;The new iPhone 17e and Pixel 10 devices&quot; style=&quot;max-width:80%&quot;/&gt;&lt;h2 id=&quot;meta-panel-improvements&quot;&gt;Meta panel improvements&lt;/h2&gt;&lt;p&gt;With support for emulating the &lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt; meta tag, we also added support for it in the Meta panel. The Meta panel now shows the &lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt; meta tag and its value, and will show a warning if the value is invalid.&lt;/p&gt;&lt;p&gt;We took this opportunity to refresh the layout of the meta panel. We removed the &amp;quot;mobile friendly&amp;quot; check (Thanks Robbert!) as it&amp;#x27;s no longer something used by Google, reordered and grouped the different lists of meta tags and added support for standard-site validation.&lt;/p&gt;&lt;h3 id=&quot;standard-site-validation&quot;&gt;Standard site validation&lt;/h3&gt;&lt;p&gt;Standard.site is a new standard for sharing publications on the at-proto protocol that&amp;#x27;s used by Bluesky. When you have it set up properly, your Bluesky sharing links will include the name and icon of your site and a button to visit it, in a color that matches your site.&lt;/p&gt;&lt;img src=&quot;static/standardsite-fd798e6af853a669d4f72d0910b3c4f8.png&quot; class=&quot;imgshadow&quot; alt=&quot;Standard.site validation panel.&quot;/&gt;&lt;p&gt;Polypane fetches your Standard Site configurations for documents and publications, validates them against the standard.site lexicon, and shows you if there are any issues.&lt;/p&gt;&lt;p&gt;The Bluesky preview then takes that data and makes it easy to see what your standard site configuration will look like when shared on Bluesky.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/standardsitepreview-6315d5ff73eecc60b466b0ea59bd1d67.png&quot; alt=&quot;The standard site preview in the meta panel&quot; style=&quot;max-width:80%&quot;/&gt;&lt;p&gt;It also supports Leaflet publications.&lt;/p&gt;&lt;h3 id=&quot;mastodon-domain-validation&quot;&gt;Mastodon domain validation&lt;/h3&gt;&lt;p&gt;Since our initial implementation of the Mastodon preview, the &lt;code class=&quot;language-text&quot;&gt;fediverse:creator&lt;/code&gt; requirements changed. You now also need a verified domain before the preview sghows your user byline.&lt;/p&gt;&lt;p&gt;Polypane now checks if the domain is verified and shows a warning explaining how to get it verified when that&amp;#x27;s missing.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/mastodon-50de2ebbd612b19c0716beed5ea94ff0.png&quot; alt=&quot;The Mastodon preview in the meta panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Manuel&lt;/strong&gt; for pointing this out to me at CSS Day!&lt;/aside&gt;&lt;h3 id=&quot;og-image-size-warnings&quot;&gt;OG image size warnings&lt;/h3&gt;&lt;p&gt;Different social media sites have different minimum and maximum dimensions and file sizes for OG images. Polypane now shows the dimensions and file size of the OG and Twitter images in the meta panel, and shows a warning if the image is larger or smaller than preferred, or when it&amp;#x27;s too small or too large to be displayed.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/largeog-a4324e149cae5ba51ade135ef14ecfaa.png&quot; alt=&quot;The OG image size warning in the meta panel&quot; style=&quot;max-width:80%&quot;/&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Ky&lt;/strong&gt; for finding this issue!&lt;/aside&gt;&lt;h3 id=&quot;robotstxt-contents&quot;&gt;Robots.txt contents&lt;/h3&gt;&lt;p&gt;Along with a parsed robots.txt that shows you the rules along with validating the current page against them, the panel now also shows the full robots.txt content.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/plaintext-5b0cae1ec7bf27bdcd3b327aede77c2d.png&quot; alt=&quot;The robots.txt contents in the meta panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;Now you can also see anything that&amp;#x27;s commented out and any other information that might be in the robots.txt file.&lt;/p&gt;&lt;h2 id=&quot;console-panel-improvements&quot;&gt;Console panel improvements&lt;/h2&gt;&lt;p&gt;The Console panel has received a few nice upgrades that make it much easier to use. As a result, evaluation is now faster too.&lt;/p&gt;&lt;h3 id=&quot;better-object-displays&quot;&gt;Better object displays&lt;/h3&gt;&lt;p&gt;We now support HTML references in arrays and object previews, so you can see the actual element in the preview, hoverable and clickable to open in the Elements panel.&lt;/p&gt;&lt;img src=&quot;static/arrayelems-612842e9de5cbafb4a265f8a9414edb6.png&quot; class=&quot;imgshadow&quot; alt=&quot;HTML references in the console panel&quot; style=&quot;max-width:400px&quot;/&gt;&lt;p&gt;This makes it much easier to see what you&amp;#x27;re working with. Object nesting support has also been improved, so you can now expand and collapse more deeply nested objects in the console panel, as well as objects inside of arrays and the other way around.&lt;/p&gt;&lt;h3 id=&quot;same-world-evaluation&quot;&gt;Same-world evaluation&lt;/h3&gt;&lt;p&gt;Polypane&amp;#x27;s console commands used to be evaluated in an &amp;#x27;isolated world&amp;#x27;, which meant that you couldn&amp;#x27;t access global variables and functions defined by you in the page&amp;#x27;s main world.&lt;/p&gt;&lt;p&gt;Console commands are now evaluated in the main world, so you can access your own variables and functions.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/sameworld-07963c09f15db88682853e5a1a352d25.png&quot; alt=&quot;The console panel in Polypane&quot; style=&quot;max-width:400px&quot;/&gt;&lt;p&gt;This also means we keep assignments around, so if you specify a &lt;code class=&quot;language-text&quot;&gt;const foo = &amp;quot;bar&amp;quot;&lt;/code&gt; in one command, you can reference it in the next command. This makes it much easier to work with the console panel.&lt;/p&gt;&lt;h2 id=&quot;accessibility-display-improvements&quot;&gt;Accessibility display improvements&lt;/h2&gt;&lt;p&gt;Did you know a button can have an invalid user entry? or that &lt;code class=&quot;language-text&quot;&gt;hr&lt;/code&gt; can set values? That&amp;#x27;s (almost) never relevant, but it&amp;#x27;s an artifact of the accessibility model.&lt;/p&gt;&lt;p&gt;We consulted a number of accessibility experts to go over some of the accessibility information we show to decide if and when we should show certain properties.&lt;/p&gt;&lt;p&gt;We now hide values that are irrelevant for the element&amp;#x27;s role in both the Elements and Outline panels, so that the information that is there is more clearly actionable and relevant.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Christian, Eric, Manuel, Sara, Jules and Ronny!&lt;/strong&gt; &lt;/aside&gt;&lt;p&gt;&lt;em&gt;I&amp;#x27;m incredibly lucky to have such a group of caring, knowledgeable, and passionate accessibility experts to consult with. Thank you all for lending your expertise and time to make Polypane better.&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;outline-panel-improvements&quot;&gt;Outline panel improvements&lt;/h2&gt;&lt;p&gt;The outline panel gathers the accessibility info more efficiently on load, making it slightly more responsive.&lt;/p&gt;&lt;h3 id=&quot;updated-focus-order-overlay&quot;&gt;Updated focus order overlay&lt;/h3&gt;&lt;p&gt;The focus order overlay and the way it calculates the tab order got some performance and correctness improvements.&lt;/p&gt;&lt;p&gt;The result is a more complete and accurate focus order that is also much faster to update as your page changes and the focusable elements get reindexed (for example, when resizing the pane or scrolling).&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/focusorder-fbf7a4efc10eb41693936ee9de69be9c.png&quot; alt=&quot;The focus order overlay in the outline panel&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;I have yet to find a tool that is as complete and accurate as Polypane&amp;#x27;s focus order overlay. For example, in the screenshot above you can see that it correctly matches the reading order, even though the DOM order is different.&lt;/p&gt;&lt;p&gt;It means that you can trust the focus order overlay when auditing, rather than you having to tab through all the (sometimes hundreds of) focusable elements to see if the tab order makes sense. You can just look at the overlay and see if the tab order is correct.&lt;/p&gt;&lt;h4 id=&quot;small-bugfix&quot;&gt;Small bugfix&lt;/h4&gt;&lt;p&gt;Lastly, we also fixed a bug where the focus overlay would sometimes mark elements as going against the reading order when they were clearly not. This was caused by our reading order logic using the wrong set of focusable elements (including programatically focusable ones) when calculating the reading order. The focus order overlay now only uses keyboard focusable elements in its calculations.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Eric&lt;/strong&gt; for mentioning this bug!&lt;/aside&gt;&lt;h2 id=&quot;panes&quot;&gt;Panes&lt;/h2&gt;&lt;p&gt;Our goal for this year is to sand away some of the edges in Polypane, the things that require Polypane-specific knowledge to understand.&lt;/p&gt;&lt;h3 id=&quot;updated-pane-header-design&quot;&gt;Updated pane header design&lt;/h3&gt;&lt;p&gt;We&amp;#x27;ve updated the design of the pane header to make the icons more clearly distinguishable and to keep things more consistent as you interact with the pane.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; alt=&quot;The updated pane header design&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;/&gt;&lt;p&gt;Icons not currently active appear dimmed, and we&amp;#x27;re distinguishing the devtools-opening icon from the others.&lt;/p&gt;&lt;p&gt;When hovering a pane header, the sizes stay where they are and the scale settings and rotate button appear to the left of them, so things no longer move around.&lt;/p&gt;&lt;img src=&quot;static/paneheaderhovered-b748898e94942c0b5ecb82d6fc0f12ce.png&quot; alt=&quot;The pane header when hovered&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;/&gt;&lt;h3 id=&quot;panes-now-zoom-to-fit-vertically&quot;&gt;Panes now zoom to fit vertically&lt;/h3&gt;&lt;p&gt;In previous versions in the horizontal and focus layouts, panes that were too tall to fit the window were clipped. This was a conscious design decision because it always kept scrolling locked in one dimension, but it was confusing when your pane wasn&amp;#x27;t the size you expected.&lt;/p&gt;&lt;video src=&quot;static/panescales-b1613a8c27f73f09a7f05bd96e208c5a.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;Starting in Polypane 30, all panes are automatically scaled down to fit the window vertically in the horizontal and focus layouts, just like they were already scaled down to fit horizontally in the vertical layout.&lt;/p&gt;&lt;p&gt;Now you always see the pane at its intended dimensions.&lt;/p&gt;&lt;h3 id=&quot;manual-scaling-with-shortcutdisplay-cmdkey-&quot;&gt;Manual scaling with &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘&lt;/kbd&gt;&lt;/h3&gt;&lt;p&gt;By holding &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘&lt;/kbd&gt; while dragging a pane&amp;#x27;s resize handle, you scale the pane instead of resizing it. This keeps the internal dimensions the same but displays the pane at a different scale.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Did you know:&lt;/strong&gt; holding &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⇧&lt;/kbd&gt; while resizing a pane will keep the aspect ratio!&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;project-improvements&quot;&gt;Project improvements&lt;/h2&gt;&lt;p&gt;Projects can now have a custom default tab, and adding bookmarks and environments to a project now automatically scrolls the newly added item into view.&lt;/p&gt;&lt;h3 id=&quot;project-specific-default-tabs&quot;&gt;Project specific default tabs&lt;/h3&gt;&lt;p&gt;The Default tab, or &lt;em&gt;homepage&lt;/em&gt;, can now be set up per project. Configure the current tab as you like (which layout, URL, set of panes, location of the panel etc), open the project settings and click &amp;quot;Set current tab as new tab&amp;quot; to set it as the default tab for that project.&lt;/p&gt;&lt;img class=&quot;imgshadow&quot; src=&quot;static/defaultnewprojecttab-704155ffa1d84d77ed99826f89e0c017.png&quot; alt=&quot;Setting the default tab for a project&quot; style=&quot;max-width:100%&quot;/&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Silas&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;p&gt;The project homepage is optional and will fall back to the globally set default new tab.&lt;/p&gt;&lt;h3 id=&quot;automatically-scroll-newly-added-bookmarks-and-environments-into-view&quot;&gt;Automatically scroll newly added bookmarks and environments into view&lt;/h3&gt;&lt;p&gt;When you add a new bookmark or environment to a project, the project editor now automatically scrolls the newly added item into view. This makes it much easier to see what you just added, especially when you have a long list of bookmarks or environments.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Eric&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;h2 id=&quot;syncing-improvements&quot;&gt;Syncing improvements&lt;/h2&gt;&lt;p&gt;Polypane&amp;#x27;s syncing engine is the most robust and complete one out there, syncing everything from scroll position to hover and focus states. But there&amp;#x27;s always room for improvement.&lt;/p&gt;&lt;p&gt;In Polypane 30 we added syncing for text selection and reset buttons.&lt;/p&gt;&lt;h3 id=&quot;text-selection-syncing&quot;&gt;Text selection syncing&lt;/h3&gt;&lt;p&gt;Your text selection is now synced across panes and portal-connected browsers.&lt;/p&gt;&lt;video src=&quot;static/textselection-e168f92fb9077a92d21921a79e5cd988.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;So if you select text, that text is now selected everywhere. Neat!&lt;/p&gt;&lt;h3 id=&quot;reset-button-syncing&quot;&gt;Reset button syncing&lt;/h3&gt;&lt;p&gt;We prevented button clicks from syncing if they were inside forms to prevent duplicate form submissions, but the way we set it up also prevented reset buttons from syncing. If you were working on a form and were counting on resetting the form in one go, that wouldn&amp;#x27;t work.&lt;/p&gt;&lt;p&gt;You probably shouldn&amp;#x27;t be using reset buttons as they&amp;#x27;re not great for UX, but if you do, they now sync across panes and portal-connected browsers. The benefit is that your form state resets everywhere.&lt;/p&gt;&lt;h2 id=&quot;area-screenshot-improvements&quot;&gt;Area screenshot improvements&lt;/h2&gt;&lt;p&gt;Creating area screenshots is now much easier. Every measurement area you draw has its own camera icon that lets you capture that area.&lt;/p&gt;&lt;video src=&quot;static/areascreenshot-b3bb6369d26ef5e3520d577643b05a57.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;Previously you had to draw a measurement area and then go to the camera menu to screenshot it. Now you can just click the camera icon to take a screenshot of that area.&lt;/p&gt;&lt;h2 id=&quot;smaller-improvements&quot;&gt;Smaller improvements&lt;/h2&gt;&lt;p&gt;For the full list there&amp;#x27;s the changelog below, but we wanted to call out a few smaller improvements that we think are worth mentioning.&lt;/p&gt;&lt;h3 id=&quot;toggle-between-last-two-panel-positions&quot;&gt;Toggle between last two panel positions&lt;/h3&gt;&lt;p&gt;You can now toggle between the last two panel positions with a new shortcut &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ ⌥ p&lt;/kbd&gt;. For example, you can toggle between the right-aligned and bottom-aligned panel with a single shortcut, or detach and reattach the panel.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Bas&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;h3 id=&quot;address-bar-interactions&quot;&gt;Address bar interactions&lt;/h3&gt;&lt;p&gt;The address bar, I keep learning, is surprisingly complex. Its text selection logic now follows other browsers:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Click + drag selects text instead of focusing the entire content&lt;/li&gt;&lt;li&gt;Click once selects the entire content&lt;/li&gt;&lt;li&gt;Click again to place the cursor at the clicked position&lt;/li&gt;&lt;li&gt;Double-click selects the word, and triple-click selects the entire content again&lt;/li&gt;&lt;/ul&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Rick&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;p&gt;We&amp;#x27;ve also updated the logic on when to start showing suggestions to align with other browsers, only starting to show suggestions after the user has started typing.&lt;/p&gt;&lt;h3 id=&quot;automatic-layout-choice-for-json-files&quot;&gt;Automatic layout choice for JSON files&lt;/h3&gt;&lt;p&gt;When you drag a JSON file to the tab bar and open it in a new tab, Polypane now automatically opens it in the full layout instead of your default layout.&lt;/p&gt;&lt;h3 id=&quot;emoji-picker-update&quot;&gt;Emoji picker update&lt;/h3&gt;&lt;p&gt;The emoji picker we use in the screenshot editor and project selector now uses Emoji 17.0, so you can use the latest emojis in your screenshots and projects.&lt;/p&gt;&lt;h3 id=&quot;context-menu-features&quot;&gt;Context menu features&lt;/h3&gt;&lt;p&gt;The context menus now let you copy email addresses without the &lt;code class=&quot;language-text&quot;&gt;mailto:&lt;/code&gt; part, telephone numbers without the &lt;code class=&quot;language-text&quot;&gt;tel:&lt;/code&gt; part, and you can now copy or save a video frame from any video.&lt;/p&gt;&lt;h2 id=&quot;chromium-152&quot;&gt;Chromium 152&lt;/h2&gt;&lt;p&gt;Polypane 30 is built on Chromium 152. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-30&quot;&gt;Get Polypane 30&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac, and Linux in 64-bit and ARM versions. Polypane automatically updates, but you can also go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14-day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-30-changelog&quot;&gt;Polypane 30 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Polypane Playgrounds&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Video recording supports sound and improved performance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Extensions: Side Panel extension support&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Network panel: filter requests&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Network panel: sort by all requests or per origin&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Emulation: &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; difference overlay is now on by default&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Emulation: &lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt; emulation support&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Emulation: Pixel and iPhone device previews&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Support &lt;code class=&quot;language-text&quot;&gt;commandForElement&lt;/code&gt; display in DOM tree (Thanks Rik!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects can now have project-specific default tabs (Thanks Silas!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Syncing engine: Synced text selection&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Meta panel: Standard Site validation and previews&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Chromium 152&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Faster startup-time due to Electron improvements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; A roughly 20% performance improvement due to Electron improvements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Network panel: hover initiator to highlight the request that initiated it (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Head order overview now recognises viewport-fit as valid value&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Redesign layout and add &lt;code class=&quot;language-text&quot;&gt;text-scale&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Show warnings for incorrectly sized OG images (Thanks Ky!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Improved fallback suggestions in OG tag lists&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Improve Google SERP preview&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Show plaintext robots.txt content (to help with comments)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Mastodon preview now checks for verified domain value (Thanks Manuel!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Update Mastodon preview colors&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Faster updating of focus overlay when scrolling and resizing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Faster accessibility tree parsing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Show a warning badge for quirks mode (Thanks Roma!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: The text editor is now resizable (Thanks Bernd!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Support &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; in CSS properties with proper indentation&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: consistent opening of Elements panel or chromium devtools under one shortcut/menu item&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: You can now click on pseudo elements in the DOM tree&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Newly supported CSS Properties and values&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Add support for focusgroup attribute autocomplete&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: Improved performance, better previews and evaluation in main world and portals&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: Element references like &lt;code class=&quot;language-text&quot;&gt;$0&lt;/code&gt; now work across shadow boundaries&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: Assignments are now kept in scope&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessibility overviews now provide additional information for properties on hover&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessibility overviews now hide irrelevant properties depending on the element role (Thanks Christian, Eric, Manuel, Sara, Jules and Ronny!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Peek: Turn touch emulation off when inspecting with Peek (Thanks Marc!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Peek: Show values of aria-current, aria-pressed and aria-expanded if defined (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; New tab: now open with the last used layout&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Rendering: the &amp;#x27;in-polypane&amp;#x27; class is now off by default&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: automatically open chrome-extension links in full layout&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Support extension id substitute strings in CSS&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Correctly support &lt;code class=&quot;language-text&quot;&gt;browser&lt;/code&gt; namespace in extensions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: support for tabCapture APIs (Thanks Kathryn!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Support for KeePassXC extension (Thanks Mateus!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Decrease logging in the terminal&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panes: now zoom out to fit in horizontal and focus layouts instead of clipping (Thanks Brady!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panes: Cmd/Ctrl + dragging now scales panes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panes: Shift + scale now follows the larger side when resizing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Image overlay: renamed from reference image&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug overlays: Renamed from Debug tools&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug overlays: Improved performance for contrast checker, text spacing, z-index and disable * debug tools&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Shortcuts: toggle Panel between last two positions (Thanks Bas!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Shortcuts: Esc no longer removed focus from pane (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; All file size notations across the app now use the same SI 1000-base units&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Context menus: Now detect email address and phone numbers for easy copying&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Context menus: Copy and save video frame options&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panel: Automatically update tab widths for larger text sizes (Thanks Ashlee!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Syncing engine: Reset buttons are now synced across panes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Project editor: Automatically scroll newly added bookmarks and environments into view (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Screenshots: Improve performance of full-page screenshots for very long pages (Thanks Brad!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Rulers: Measure areas now have a camera icon to create screenshots of the measured area&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Address bar: You can now click + drag to select text, instead of it focusing the entire content (Thanks Rick!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Emoji picker: Now supports Emoji 17.0&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Polypane Peek: disables scroll syncing on inspect&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Dragged-in JSON files automatically open in full layouts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; JSON and XML viewers: improve dark mode styling&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Passkey support should now work with more authentication methods&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated list of Google Fonts&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Removed&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; Meta panel: The &amp;quot;mobile friendly&amp;quot; check is removed as it&amp;#x27;s no longer used by search engines (Thanks Robbert!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Removed&lt;/strong&gt; A11y.css is now available as a snippet and no longer part of the Debug overlays&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: Whatsapp card was missing title&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: Opening a new tab no longer resets preview order&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Network panel: Sort origins by first connection time, rather than last activity (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Network panel: Web Vitals data was cleared when switching tabs (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Network panel: Prevent resetting of size bars&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Fix missing computed styles for some websites (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Properly expand attribute value fields for long values&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Show nested declarations in @scope rules (Thanks Rick!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Peek: the color preview for &lt;code class=&quot;language-text&quot;&gt;color-mix()&lt;/code&gt; was broken (Thanks Marc!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Pane URL bar: prevent selecting a preset on enter and applying URL on blur&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Polypane Message Bus: Message bus is now available again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Console panel: Prevent console from getting its element references from different tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Reference image: Properly support non-ascii characters (Thanks Sigmundur!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Panes: Fix sizer position for zoomed out panes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Debug overlays: Disable Images now correctly restores when turned off&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Debug overlays: Right-click no longer re-applies debug overlays twice&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Address bar: Pressing enter on an empty address bar no longer submits&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Address bar: Make sure the address bar doesn&amp;#x27;t overlap warning icons (Thanks Vadim!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Focus overlay correctly ignores tabindex=-1 elements for comparisons&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: styling issue fixes for warnings and indicators&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Screenshots: Fix issue where images weren&amp;#x27;t saved to disk directly&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Screenshots: Prevent visual glitch when taking a full height screenshot&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Screenshots: Prevent silent error when trying to save to a non-existent folder&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; and safe area insets now redraw faster during resize&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Google Fonts import didn&amp;#x27;t use variable fonts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Extensions: Automatically support default permissions for all extensions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Middle click ruler button on full layout now clears the rulers&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Portal only syncs events from the portalled tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Portal syncs non-tab originated navigation events again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Focus overlay now only uses tabbable elements for tab order validation instead of all focusable elements (Thanks Eric!)&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Cheatsheets for flex, grid, anchor positioning and invoker commands]]></title><description><![CDATA[For CSS Day this year we created a set of cheatsheets explaining Flexbox, Grid, Anchor positioning and Invoker commands. They came out…]]></description><link>https://polypane.app/blog/cheatsheets-for-flex-grid-anchor-positioning-and-invoker-commands/</link><guid isPermaLink="false">https://polypane.app/blog/cheatsheets-for-flex-grid-anchor-positioning-and-invoker-commands/</guid><pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For CSS Day this year we created a set of cheatsheets explaining Flexbox, Grid, Anchor positioning and Invoker commands.&lt;/p&gt;&lt;p&gt;They came out beautifully and a lot of people really liked them at our booth, so we decided to make them available as free downloads at &lt;a href=&quot;/cheatsheets&quot;&gt;polypane.app/cheatsheets&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;css-day-swag&quot;&gt;CSS Day swag&lt;/h2&gt;&lt;p&gt;This year we had a booth at CSS Day in Amsterdam, and it was a ton of fun to talk to so many Polypane users (the crowd as well as the speaker line-up was full of them!) and to meet so many people for who Polypane was new and who were excited to try it out.&lt;/p&gt;&lt;img src=&quot;static/booth-f8e5e32d68e5099e6b3969fab9148cfa.webp&quot; class=&quot;imgshadow&quot; alt=&quot;Polypane booth at CSS Day 2026&quot;/&gt;&lt;p&gt;Of course we made sure to bring stickers, badges and pins, but for our booth at CSS Day we also wanted to create some swag that wasn&amp;#x27;t just &amp;#x27;thing with our logo&amp;#x27;, but something that you&amp;#x27;d actually want to keep and use.&lt;/p&gt;&lt;p&gt;Having recently struggled a lot with Anchor positioning and always forgetting the exact syntax for Grid areas made those easy topics to pick because, if nothing else, we would find them useful ourselves.&lt;/p&gt;&lt;p&gt;Flex to go along with Grid and the very exciting new Invoker commands were natural choices to go along with those two, so that&amp;#x27;s how we ended up with the four cheatsheets we made.&lt;/p&gt;&lt;img src=&quot;static/cssday-2026-27-8725c36ce0e0efb778fbc137c3026dc8.jpg&quot; class=&quot;imgshadow&quot; alt=&quot;Someone holding an Anchor positioning cheatsheet&quot;/&gt;&lt;i&gt;Photo by &lt;a href=&quot;https://richardtheemling.com/&quot;&gt;Richard Theemling&lt;/a&gt; for CSS Day&lt;/i&gt;&lt;p&gt;The cheatsheets aren&amp;#x27;t Polypane-specific, they&amp;#x27;re just nice-looking references for these topics that (for the print versions) you can keep on your desk and refer to for a quick lookup of &amp;#x27;how to do that thing again&amp;#x27;.&lt;/p&gt;&lt;img src=&quot;static/cssday-2026-100-bae1148c23f55bc4dd031c24ca07fbb3.jpg&quot; class=&quot;imgshadow&quot; alt=&quot;Someone holding an Invokers cheatsheet&quot;/&gt;&lt;i&gt;Photo by &lt;a href=&quot;https://richardtheemling.com/&quot;&gt;Richard Theemling&lt;/a&gt; for CSS Day&lt;/i&gt;&lt;h2 id=&quot;the-cheatsheets&quot;&gt;The cheatsheets&lt;/h2&gt;&lt;p&gt;We had the cheatsheets printed on very nice card stock with a matte finish and rounded corners and wanted to make sure at least some of that came across in the online version, so as you pick one of the cards on &lt;a href=&quot;/cheatsheets&quot;&gt;polypane.app/cheatsheets&lt;/a&gt; you&amp;#x27;ll see a nice rendering of the card with the same matte finish as you mouse over it. Definitely recommend checking out that page (and bookmarking it for later) if you want to see the cards in all their glory.&lt;/p&gt;&lt;p&gt;If you just want to download them, you can scroll down to download them directly from this page. All downloads are free and print-ready (they have a 3mm bleed and are in A5 format).&lt;/p&gt;&lt;h3 id=&quot;flexbox-cheatsheet&quot;&gt;Flexbox Cheatsheet&lt;/h3&gt;&lt;img src=&quot;static/flex-cdebde34ae2dd8271389266d9f19cb6b.png&quot; alt=&quot;Flexbox Cheatsheet&quot; class=&quot;imgshadow&quot; style=&quot;max-width:80%;border-radius:0.5rem&quot;/&gt;&lt;div style=&quot;display:flex;gap:1rem;flex-wrap:wrap;justify-content:center&quot;&gt;&lt;a href=&quot;static/flex-73b0fff29fc5538ae172ae31947af3c2.pdf&quot; download=&quot;Flexbox Cheatsheet.pdf&quot; class=&quot;download-button&quot;&gt;Download as PDF&lt;/a&gt;&lt;a href=&quot;static/flex-cdebde34ae2dd8271389266d9f19cb6b.png&quot; download=&quot;Flexbox Cheatsheet.png&quot; class=&quot;download-button&quot;&gt;Download as PNG&lt;/a&gt;&lt;/div&gt;&lt;h3 id=&quot;grid-cheatsheet&quot;&gt;Grid Cheatsheet&lt;/h3&gt;&lt;img src=&quot;static/grid-039c02206636cf9ec6e0087a8014b176.png&quot; alt=&quot;Grid Cheatsheet&quot; class=&quot;imgshadow&quot; style=&quot;max-width:80%;border-radius:0.5rem&quot;/&gt;&lt;div style=&quot;display:flex;gap:1rem;flex-wrap:wrap;justify-content:center&quot;&gt;&lt;a href=&quot;static/grid-31aabd8a0defe297516eab6e83f6a6a8.pdf&quot; download=&quot;Grid Cheatsheet.pdf&quot; class=&quot;download-button&quot;&gt;Download as PDF&lt;/a&gt;&lt;a href=&quot;static/grid-039c02206636cf9ec6e0087a8014b176.png&quot; download=&quot;Grid Cheatsheet.png&quot; class=&quot;download-button&quot;&gt;Download as PNG&lt;/a&gt;&lt;/div&gt;&lt;h3 id=&quot;anchor-cheatsheet&quot;&gt;Anchor Cheatsheet&lt;/h3&gt;&lt;img src=&quot;static/anchor-9f716079954685439a1fbd2adb401147.png&quot; alt=&quot;Anchor Cheatsheet&quot; class=&quot;imgshadow&quot; style=&quot;max-width:80%;border-radius:0.5rem&quot;/&gt;&lt;div style=&quot;display:flex;gap:1rem;flex-wrap:wrap;justify-content:center&quot;&gt;&lt;a href=&quot;static/anchor-c5fba749af692527673d7578a56ef3a8.pdf&quot; download=&quot;Anchor Cheatsheet.pdf&quot; class=&quot;download-button&quot;&gt;Download as PDF&lt;/a&gt;&lt;a href=&quot;static/anchor-9f716079954685439a1fbd2adb401147.png&quot; download=&quot;Anchor Cheatsheet.png&quot; class=&quot;download-button&quot;&gt;Download as PNG&lt;/a&gt;&lt;/div&gt;&lt;h3 id=&quot;invoker-cheatsheet&quot;&gt;Invoker Cheatsheet&lt;/h3&gt;&lt;img src=&quot;static/invoker-60bf6124890ec332041ee90bde4e2f76.png&quot; alt=&quot;Invoker Cheatsheet&quot; class=&quot;imgshadow&quot; style=&quot;max-width:80%;border-radius:0.5rem&quot;/&gt;&lt;div style=&quot;display:flex;gap:1rem;flex-wrap:wrap;justify-content:center&quot;&gt;&lt;a href=&quot;static/invoker-a80e44a4a378f3a49d682b7a14d06fcc.pdf&quot; download=&quot;Invoker Cheatsheet.pdf&quot; class=&quot;download-button&quot;&gt;Download as PDF&lt;/a&gt;&lt;a href=&quot;static/invoker-60bf6124890ec332041ee90bde4e2f76.png&quot; download=&quot;Invoker Cheatsheet.png&quot; class=&quot;download-button&quot;&gt;Download as PNG&lt;/a&gt;&lt;/div&gt;&lt;h2 id=&quot;want-them-printed-and-shipped-to-you&quot;&gt;Want them printed and shipped to you?&lt;/h2&gt;&lt;p&gt;We&amp;#x27;re planning on making them available as printed A5 cards in the future, so if you&amp;#x27;d like to be notified when that happens, please &lt;a href=&quot;/newsletter&quot;&gt;sign up for our newsletter&lt;/a&gt; and we&amp;#x27;ll let you know when they&amp;#x27;re available.&lt;/p&gt;&lt;p&gt;Or catch us at a conference and we&amp;#x27;ll be happy to give you a set of printed cards for free!&lt;/p&gt;&lt;h2 id=&quot;got-suggestions&quot;&gt;Got suggestions?&lt;/h2&gt;&lt;p&gt;If you have any suggestions for other cheatsheets you&amp;#x27;d like to see, or if you have any feedback on the ones we made, please &lt;a href=&quot;/support&quot;&gt;let us know&lt;/a&gt;!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Experimenting with the new pure CSS random() function]]></title><description><![CDATA[CSS is getting a  random()  function that lets you set properties with a random value, opening up a ton of interesting creative…]]></description><link>https://polypane.app/blog/experimenting-with-the-new-pure-css-random-function/</link><guid isPermaLink="false">https://polypane.app/blog/experimenting-with-the-new-pure-css-random-function/</guid><pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;CSS is getting a &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; function that lets you set properties with a random value, opening up a ton of interesting creative possibilities. In this article, I’m taking an early look at the function to see how it works and where the gotchas are (and there are a few) and really putting it through its paces.&lt;/p&gt;&lt;p&gt;I built seven different demos that you can play around with and edit yourself in the interactive examples below: bokeh, falling petals a stack of fling-able polaroids, a visual poem, a shaky grid, rotating rectangles and finally, recreating an aurora.&lt;/p&gt;&lt;p&gt;You probably don’t want your UI to be random every time someone loads the page. There are plenty of places where a little randomness, in for example a small animation, on a button click or something as simple as how a popup appears, can elevate the entire experience. Having it be easily accessible in CSS is really exciting, and a big win for designers and developers alike. The demos here are big and highly visual to show off the possibilities, I hope they inpire you to think about how you can use randomness in your own designs.&lt;/p&gt;&lt;video src=&quot;static/video-c76d19a2855a85f17722d262c185a883.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;h2 id=&quot;a-note-on-the-code-examples&quot;&gt;A note on the code examples&lt;/h2&gt;&lt;p&gt;We&amp;#x27;re using &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; directly in the code examples below, and don&amp;#x27;t have a fallback for browsers that don&amp;#x27;t support it yet. If you want to see the examples in action, you can &lt;a href=&quot;https://dashboard.polypane.app/register/&quot;&gt;get Polypane&lt;/a&gt; and try it for free.&lt;/p&gt;&lt;p&gt;The code examples have fully editable CSS, but only show the relevant CSS for each effect (for example the bokeh below does show the positioning, but not the &lt;code class=&quot;language-text&quot;&gt;border-radius&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;You can edit the CSS in the examples to see how it works. In the code example we only show the most relevant CSS, but if you copy the code you&amp;#x27;ll get the full HTML and CSS for the example. Click the &amp;#x27;randomize&amp;#x27; button to reload the example with a new random value for each property.&lt;/p&gt;&lt;h2 id=&quot;a-note-on-browser-support&quot;&gt;A note on browser support&lt;/h2&gt;&lt;p&gt;We&amp;#x27;re using &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; directly in the examples below without fallbacks, so many of these demos currently only work properly in Polypane, which we used to build and test them. Chromium 148+ also supports &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; behind the Experimental Web Platform Features flag. Safari 26.2+ has partial support and several demos do not work correctly there yet. Safari 26.2+ support &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;, but doesn&amp;#x27;t fully support all the demos I show in this article. Firefox currently has no support for &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;For the best experience, including the editable live demos and the most complete implementation, open this article in &lt;a href=&quot;https://dashboard.polypane.app/register/&quot;&gt;Polypane 29+&lt;/a&gt; and try the examples yourself.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Some demos may behave differently or fail entirely outside Polypane right now. We&amp;#x27;ll keep the article updated as the spec and implementations evolve. If you&amp;#x27;re in the future right now and notice some of the demos no longer work, please let us know and we&amp;#x27;ll update the article!&lt;/p&gt;&lt;h2 id=&quot;my-first-experiment-bokeh-effect&quot;&gt;My first experiment: Bokeh effect&lt;/h2&gt;&lt;p&gt;I first learned about the availability of &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; in browsers when I saw it listed in the newly available experimental features in Chromium 148, as I was implementing that in &lt;a href=&quot;https://polypane.app/experimental-web-platform-features/?search=random&quot;&gt;Polypane 29&lt;/a&gt;. Whenever I think of visual randomness, bokeh is the first thing that comes to mind.&lt;/p&gt;&lt;p&gt;Bokeh is a photography effect that creates out-of-focus light spots, and I find it absolutely mesmerizing to look at. I thought it would be fun to try to create a bokeh effect using &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;. So I grabbed the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/random&quot;&gt;MDN page on random()&lt;/a&gt; and got to work.&lt;/p&gt;&lt;p&gt;If you look at a bokeh effect, it&amp;#x27;s essentially a bunch of circles that differ in a few ways:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;the position of the circles&lt;/li&gt;&lt;li&gt;the size of the circles&lt;/li&gt;&lt;li&gt;the color of the circles&lt;/li&gt;&lt;li&gt;the out-of-focus-ness of the circles&lt;/li&gt;&lt;li&gt;the rate at which they change (if it&amp;#x27;s an animated bokeh)&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;position&quot;&gt;Position&lt;/h3&gt;&lt;p&gt;The position of the circles is easiest: set them to position:absolute, and then use &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt; properties. This will place the circles at random positions:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Random Position&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Random Position&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }


      .bokeh {
  position:absolute;
  left: random(0%, 100%);
  top: random(0%, 100%);
  border-radius: 100%;
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:176px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  position:absolute;
  left: random(0%, 100%);
  top: random(0%, 100%);
  border-radius: 100%;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  position:absolute;
  left: random(0%, 100%);
  top: random(0%, 100%);
  border-radius: 100%;
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;The CSS in these examples is live and interactive. Want to experiment with these examples live? Open this article &lt;a href=&quot;https://dashboard.polypane.app/register/&quot;&gt;in Polypane&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; function in its simplest form takes a minimum (0%) and a maximum (100%) and returns a random value between those values.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The minimum size here is 0, but it has to include the % to make sure both values are of the same type. If you just put &lt;code class=&quot;language-text&quot;&gt;random(0, 100%)&lt;/code&gt;, it would be invalid because the minimum is a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;number&amp;gt;&lt;/code&gt; and the maximum is a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;percentage&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;size&quot;&gt;Size&lt;/h3&gt;&lt;p&gt;Each circle in a bokeh effect has a different size, but it&amp;#x27;s still a circle. So if we want to use &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; to set the size, we need to make sure that the width and height are the same value.&lt;/p&gt;&lt;p&gt;My idea was to &amp;#x27;store&amp;#x27; the random value in a custom property (like &lt;code class=&quot;language-text&quot;&gt;--size: random(50px, 20vmin)&lt;/code&gt;) and then use that custom property for both the width and height. That way, the width and height will always be the same value.&lt;/p&gt;&lt;p&gt;Or so I thought.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Random Ovals&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Random Ovals&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }


      .bokeh {
  left: random(0%, 100%);
  top: random(0%, 100%);

  --size: random(50px, 20vmin);
  width: var(--size);
  height: var(--size);
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:224px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  left: random(0%, 100%);
  top: random(0%, 100%);

  --size: random(50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  left: random(0%, 100%);
  top: random(0%, 100%);

  --size: random(50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Yikes, width and height are different!&lt;/p&gt;&lt;p&gt;When you set a custom property to a random value, it will generate a new random value &lt;em&gt;every time&lt;/em&gt; you &lt;strong&gt;use&lt;/strong&gt; that custom property, not just when you declare it.&lt;/p&gt;&lt;p&gt;To fix this, we have to add a custom keyword to the random function called &lt;code class=&quot;language-text&quot;&gt;element-scoped&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;This tells the browser to generate one random value &lt;em&gt;per element&lt;/em&gt;. So each element gets a different random value, but the width and height of each element will be the same:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Size Fixed With element-scoped&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Size Fixed With element-scoped&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }


      .bokeh {
  top: random(0%, 100%);
  left: random(0%, 100%);

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:224px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  top: random(0%, 100%);
  left: random(0%, 100%);

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  top: random(0%, 100%);
  left: random(0%, 100%);

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;Note: &lt;code class=&quot;language-text&quot;&gt;element-scoped&lt;/code&gt; is not the final name for this feature, but it&amp;#x27;s the one currently supported in Polypane and Safari. It will be renamed to &lt;code class=&quot;language-text&quot;&gt;per-element&lt;/code&gt;. Neither is yet part of the MDN docs, though &lt;a href=&quot;https://github.com/mdn/content/pull/44544&quot;&gt;a PR has been submitted&lt;/a&gt; to add it.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;position-revisited&quot;&gt;Position, revisited&lt;/h3&gt;&lt;p&gt;I wanted to start with the position because it was simplest, but the position actually has an issue: if a circle starts at 99% from the left and has a size of 100px, it will overflow the container and cause scrollbars.&lt;/p&gt;&lt;p&gt;To fix this, we can use &lt;code class=&quot;language-text&quot;&gt;calc()&lt;/code&gt; to subtract the size of the circle from the position:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Size Fixed With element-scoped&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Size Fixed With element-scoped&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }


      .bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:224px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));

  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;We don&amp;#x27;t know exactly what the size of the circle is, but because &lt;code class=&quot;language-text&quot;&gt;--size&lt;/code&gt; is the same for this element in each property, we can use it to be sure that the circles are always fully within the container.&lt;/p&gt;&lt;h3 id=&quot;the-color-of-the-circles&quot;&gt;The color of the circles&lt;/h3&gt;&lt;p&gt;For the colors of the circles, I wanted all of them to be roughly the same lightness and saturation, but each circle to have a different hue. So I used the &lt;code class=&quot;language-text&quot;&gt;hsl()&lt;/code&gt; function with &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; for the hue value.&lt;/p&gt;&lt;p&gt;Additionally, when you look at images of bokeh, some circles are more visible and some are less visible. So I also added a random opacity:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Color and Opacity&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Color and Opacity&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }


      .bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));
  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);

  background: hsl(random(0, 360) 80% 50%);
  opacity: random(0.1, 1);
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:272px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));
  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);

  background: hsl(random(0, 360) 80% 50%);
  opacity: random(0.1, 1);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  left: random(0%, calc(100% - var(--size)));
  top: random(0%, calc(100% - var(--size)));
  --size: random(element-scoped, 50px, 20vmin);
  width: var(--size);
  height: var(--size);

  background: hsl(random(0, 360) 80% 50%);
  opacity: random(0.1, 1);
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;hue&lt;/code&gt; in HSL is just a number, so it doesn&amp;#x27;t need a unit. The same goes for opacity.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Note: We&amp;#x27;ve increased the number of circles in this and the next few examples so the effect is more clearly visible.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;out-of-focus-ness&quot;&gt;Out-of-focus-ness&lt;/h3&gt;&lt;p&gt;Next up, some of the circles in a bokeh image are really sharp while others are more blurry.&lt;/p&gt;&lt;p&gt;In CSS, we can achieve a blur effect with the &lt;code class=&quot;language-text&quot;&gt;filter: blur()&lt;/code&gt; function. When you add a blur you also lose some of the vibrancy that some of the colors have, so I also added a &lt;code class=&quot;language-text&quot;&gt;contrast()&lt;/code&gt; filter to make some of the circles more vibrant.&lt;/p&gt;&lt;p&gt;Lastly, I added a &lt;code class=&quot;language-text&quot;&gt;mix-blend-mode&lt;/code&gt; to make the circles blend together using &amp;quot;plus-lighter&amp;quot;, That increases the brightness of the circles where they overlap, which makes it look more like real bokeh:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Blur, Contrast, Blend&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Bokeh: Blur, Contrast, Blend&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  
  body {
    position: absolute;
    inset: 0;
    margin: 0;
    overflow: hidden;
  }

  .bokeh {
    position: absolute;
    border-radius: 100%;
    width: 70px;
    height: 70px;
    background: #fff8;
    z-index: random(1, 100);
  }

  .bokeh {
    --size: random(element-scoped, 50px, 20vmin);
    width: var(--size);
    height: var(--size);
    left: random(0%, calc(100% - var(--size)));
    top: random(0%, calc(100% - var(--size)));
    background: hsl(random(0, 360) 80% 50%);
    opacity: random(0.1, 1);
  }


      .bokeh {
  filter: blur(random(2px, 6px)) contrast(random(50%, 200%));
  mix-blend-mode: plus-lighter;
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;bokeh&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:128px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  filter: blur(random(2px, 6px)) contrast(random(50%, 200%));
  mix-blend-mode: plus-lighter;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  filter: blur(random(2px, 6px)) contrast(random(50%, 200%));
  mix-blend-mode: plus-lighter;
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;That blur helps make the circles look more like actual bokeh circles, and the contrast makes some of them look more vibrant and some of them look more dull, which adds to the effect.&lt;/p&gt;&lt;p&gt;For contrast we specifically chose a range where most random values would &lt;em&gt;increase&lt;/em&gt; the contrast (the 100% to 200% range) and only a handful of circles would have a contrast value that decreases the contrast (the 50% to 100% range).&lt;/p&gt;&lt;h3 id=&quot;animation&quot;&gt;Animation&lt;/h3&gt;&lt;p&gt;Finally, I wanted to add some animation to the bokeh circles. When you look at bokeh through a camera and you increase and decrease the focus, some circles become larger and more blurry, while others become smaller and sharper.&lt;/p&gt;&lt;p&gt;The easiest way to do that is with a simple pulse animation. We only need to define the 50% keyframe. The 0% and 100% keyframes will be the default state of the element (a scale of 1).&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; pulse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;50%&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can re-use this same animation for all circles, because we can set the animation timing and the animation delay to a random value for each circle. This way, each circle will pulse at a different rate and start at a different time, which makes the animation look more natural:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.bokeh&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pulse &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2s&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 5s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; ease-in-out infinite&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;animation-delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-5s&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We let the animation run infinitely so it just keeps looping with an ease-in-out timing function to smooth out the start and end of each pulse. By setting a random &lt;em&gt;negative&lt;/em&gt; delay, some circles will start the animation somewhere in the middle, which makes it look more natural as you refresh the page. If we only used no delay or a positive delay, all circles would start their animation by growing.&lt;/p&gt;&lt;h3 id=&quot;combining-it-all&quot;&gt;Combining it all&lt;/h3&gt;&lt;p&gt;So when we combine all of these properties together, we get this:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Bokeh: Final Combined Demo&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:320px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.bokeh {
  filter: blur(random(2px, 6px)) contrast(random(50%, 200%));
  mix-blend-mode: plus-lighter;
  animation: pulse random(2s, 5s) ease-in-out infinite;
  animation-delay: random(-5s, 0s);
}

@keyframes pulse {
  50% {
    scale: 1.2;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.bokeh {
  filter: blur(random(2px, 6px)) contrast(random(50%, 200%));
  mix-blend-mode: plus-lighter;
  animation: pulse random(2s, 5s) ease-in-out infinite;
  animation-delay: random(-5s, 0s);
}

@keyframes pulse {
  50% {
    scale: 1.2;
  }
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; For animations like the one above that have animations, we show a &amp;quot;show demo&amp;quot; button to activate the preview, for two reasons: 1) the animation can be distracting or annoying and 2) it can be performance-intensive.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;falling-flower-petals&quot;&gt;Falling flower petals&lt;/h2&gt;&lt;p&gt;We have a cherry tree in our backyard, and when the wind blows in spring, the petals fall off the tree and float down to the ground. It&amp;#x27;s very soothing.&lt;/p&gt;&lt;p&gt;I thought it would be fun to try to recreate that effect using &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;, and lets front-load the final animation so you can see what we&amp;#x27;re trying to achieve:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Falling Petals&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; In Safari sometimes you don&amp;#x27;t see any petals in the demos. If that happens, click randomize a few times. I don&amp;#x27;t know why this happens, but it seems to be a Safari bug.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Lets look at what parts need to be random to achieve this result.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The petals all need to start at a different position off-screen&lt;/li&gt;&lt;li&gt;They should all be a slightly different size and should look a little different&lt;/li&gt;&lt;li&gt;They all need to move vaguely in the same direction as if blown by the wind, but each petal should fall slightly differently&lt;/li&gt;&lt;li&gt;They should fall at different speeds&lt;/li&gt;&lt;li&gt;They should randomly twirl as they fall&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;starting-position&quot;&gt;Starting position&lt;/h3&gt;&lt;p&gt;The starting position of each petal should be above the current viewport, and then randomly across the width of the viewport so that they each fall from a different place.&lt;/p&gt;&lt;p&gt;For the &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt; value we can use a fixed one so they all start at the same place, and then for the horizontal starting value, we&amp;#x27;ll use a custom property, since we&amp;#x27;re going to reuse it once we get to the falling animation.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ll use &lt;code class=&quot;language-text&quot;&gt;element-scoped&lt;/code&gt; so that each petal has a different starting position, and we&amp;#x27;re not going to set &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;, but &lt;code class=&quot;language-text&quot;&gt;translate&lt;/code&gt; because we want to animate the petals falling down the screen, and animating &lt;code class=&quot;language-text&quot;&gt;translate&lt;/code&gt; is more performant than animating &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.petal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--initial-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element-scoped&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -40vw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100vw&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--initial-position&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;#x27;d show a demo, but they all start off-screen and don&amp;#x27;t animate, so you wouldn&amp;#x27;t see anything. That&amp;#x27;s our starting position sorted, lets move on to size and shape.&lt;/p&gt;&lt;h3 id=&quot;size-and-petal-style&quot;&gt;Size and petal style&lt;/h3&gt;&lt;p&gt;For the size, lets use &lt;code class=&quot;language-text&quot;&gt;aspect-ratio&lt;/code&gt; instead of an explicit &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; this time so we can keep things simple:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.petal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;30px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1/1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the petals themselves I found four different petals by &lt;a href=&quot;https://www.vecteezy.com/members/pram106?uploads_term=cherry+petal&quot;&gt;Pram Samnak&lt;/a&gt; that I liked and wanted to randomly pick one from.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Theoretically&lt;/em&gt; you would be able to use &lt;code class=&quot;language-text&quot;&gt;random-item()&lt;/code&gt; to randomly pick one of the four petals, but that function is not yet implemented in any browser. It would look like this, if it worked:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.petal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random-item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/1.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/2.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/3.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/4.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I had to think a while on how to get around this, and I ended up realising that if each petal starts at a random place I could just use &lt;code class=&quot;language-text&quot;&gt;nth-child&lt;/code&gt;,&lt;/p&gt;&lt;p&gt;The repeating pattern of the &lt;code class=&quot;language-text&quot;&gt;nth-child()&lt;/code&gt; selector wouldn&amp;#x27;t be noticeable since each petal starts at a random position.&lt;/p&gt;&lt;p&gt;All of this still happens off-screen so there&amp;#x27;s no demo. Let&amp;#x27;s quickly add the falling animation and see something!&lt;/p&gt;&lt;h3 id=&quot;falling-animation&quot;&gt;Falling animation&lt;/h3&gt;&lt;p&gt;When petals fall down they also get pushed around by the wind, so they don&amp;#x27;t fall straight down. To achieve this, we can animate &lt;code class=&quot;language-text&quot;&gt;translate&lt;/code&gt; and move the petals down and to the right.&lt;/p&gt;&lt;p&gt;Because each petal catches the wind differently, we want them to fall at different speeds and at different angles. For the speed, we can vary the duration of the animation. For the angle we can set an end value for the &lt;code class=&quot;language-text&quot;&gt;translate&lt;/code&gt; that takes the &lt;code class=&quot;language-text&quot;&gt;--initial-position&lt;/code&gt; that we already had, and add a random amount.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Falling Petals: Fall Animation&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:368px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.petal {
  --initial-position: random(element-scoped, -40vw, 100vw);
  --end-position: calc(var(--initial-position) + random(20vw, 50vw));

  translate: var(--initial-position) 0;
  animation: fall random(5s, 10s) linear infinite;
  animation-delay: random(-10s, 0s);
}

@keyframes fall {
  to {
    translate: var(--end-position) calc(100vh + 100px);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.petal {
  --initial-position: random(element-scoped, -40vw, 100vw);
  --end-position: calc(var(--initial-position) + random(20vw, 50vw));

  translate: var(--initial-position) 0;
  animation: fall random(5s, 10s) linear infinite;
  animation-delay: random(-10s, 0s);
}

@keyframes fall {
  to {
    translate: var(--end-position) calc(100vh + 100px);
  }
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Just like with the bokeh example, Each petal has a different timing and we set a negative delay so that some petals start in the middle of their animation, which makes it look more natural as you load the page.&lt;/p&gt;&lt;p&gt;We only have to set the &lt;code class=&quot;language-text&quot;&gt;to&lt;/code&gt; keyframe to the end position at the bottom of the screen. When the petal leaves the screen, it jumps back to the top and starts falling again, creating a continuous effect.&lt;/p&gt;&lt;h3 id=&quot;twirling-animation&quot;&gt;Twirling animation&lt;/h3&gt;&lt;p&gt;To achieve the idea of the petals twirling in 3D, we scale them randomly as they fall as if they are moving closer and further away from the camera, and we rotate them along the X, Y and Z axis.&lt;/p&gt;&lt;p&gt;We use two separate animations for that, so we can have both of these happen at different random durations and starting times to make a unique animation for each petal.&lt;/p&gt;&lt;p&gt;For the rotation we use transforms with &lt;code class=&quot;language-text&quot;&gt;perspective()&lt;/code&gt; to get a nicer looking 3D effect on the petals. We also want each petal to twirl at a different speed, so we also use &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; in the animation duration.&lt;/p&gt;&lt;p&gt;Lastly, we set the transform-origin to the &lt;code class=&quot;language-text&quot;&gt;top center&lt;/code&gt; of the element, so that it twirls around that point rather than around the center of the petal.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Falling Petals: Twirling&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:920px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.petal {
  --initial-position: random(element-scoped, -40vw, 100vw);
  --end-position: calc(var(--initial-position) + random(20vw, 50vw));
  --rotx: random(-45deg, 45deg);
  --roty: random(-45deg, 45deg);

  translate: var(--initial-position) 0;

  animation:
    fall random(5s, 10s) linear infinite,
    rotate random(2s, 4s) linear infinite,
    grow random(2s, 4s) ease-in-out infinite alternate;
  animation-delay: random(-10s, 0s), random(-10s, 0s), random(-10s, 0s);
  transform-origin: top center;
}

@keyframes fall {
  to {
    translate: var(--end-position) calc(100vh + 100px);
  }
}

@keyframes grow {
  to {
    scale: random(1.5, 2);
  }
}

@keyframes rotate {
  from {
    transform: perspective(300px) rotateY(var(--roty)) rotateZ(-30deg) rotateX(var(--rotx));
  }
  to {
    transform: perspective(300px) rotateY(calc(var(--roty) + 360deg)) rotateZ(330deg)
      rotateX(calc(var(--rotx) + 360deg));
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.petal {
  --initial-position: random(element-scoped, -40vw, 100vw);
  --end-position: calc(var(--initial-position) + random(20vw, 50vw));
  --rotx: random(-45deg, 45deg);
  --roty: random(-45deg, 45deg);

  translate: var(--initial-position) 0;

  animation:
    fall random(5s, 10s) linear infinite,
    rotate random(2s, 4s) linear infinite,
    grow random(2s, 4s) ease-in-out infinite alternate;
  animation-delay: random(-10s, 0s), random(-10s, 0s), random(-10s, 0s);
  transform-origin: top center;
}

@keyframes fall {
  to {
    translate: var(--end-position) calc(100vh + 100px);
  }
}

@keyframes grow {
  to {
    scale: random(1.5, 2);
  }
}

@keyframes rotate {
  from {
    transform: perspective(300px) rotateY(var(--roty)) rotateZ(-30deg) rotateX(var(--rotx));
  }
  to {
    transform: perspective(300px) rotateY(calc(var(--roty) + 360deg)) rotateZ(330deg)
      rotateX(calc(var(--rotx) + 360deg));
  }
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To finish it off, we got a &lt;a href=&quot;https://www.vecteezy.com/photo/69702118-serene-spring-blossoms-under-a-cloudy-sky&quot;&gt;nice background image&lt;/a&gt; as a background.&lt;/p&gt;&lt;h2 id=&quot;a-stack-of-polaroids-you-can-fling-away&quot;&gt;A stack of Polaroids you can fling away&lt;/h2&gt;&lt;p&gt;For the third example, I want to create an &amp;quot;untidy&amp;quot; stack of Polaroid photos, where they&amp;#x27;re all slightly rotated and offset from each other to create a natural looking pile. Additionally, i wanted to fling them in random directions when you click on them.&lt;/p&gt;&lt;p&gt;Let&amp;#x27;s look at how we can add interactivity to our randomness!&lt;/p&gt;&lt;h3 id=&quot;building-the-stack&quot;&gt;Building the stack&lt;/h3&gt;&lt;p&gt;The easiest way to create a stack of elements is to use CSS Grid and place all the elements in the same grid cell:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;place-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.polaroid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;grid-area&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1/1/2/2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then we can style each polaroid. This time they all have the same dimensions but it&amp;#x27;s the transform that we&amp;#x27;re going to use to add some randomness.&lt;/p&gt;&lt;p&gt;The HTML structure of each polaroid looks like this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroid&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;img&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroids/1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because of the grid they&amp;#x27;re already all stacked in the same position on top of each other. We can use &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt; property to rotate and offset each card randomly:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Polaroids: Randomized Stack&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Polaroids: Randomized Stack&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  * { box-sizing: border-box; }
  :root {
    --photo-1: url(&amp;quot;static/christoffer-engstrom-wc9avd2RaN0-unsplash-54d4c13a19c68cb52bce51077cf747e8.jpg&amp;quot;);
    --photo-2: url(&amp;quot;static/conor-sexton-hRemch0ZDwI-unsplash-882547c037e9a4523fa14c791d85f855.jpg&amp;quot;);
    --photo-3: url(&amp;quot;static/cristian-palmer-XexawgzYOBc-unsplash-a50326deaa4daf7df939b28145544f5d.jpg&amp;quot;);
    --photo-4: url(&amp;quot;static/giga-khurtsilava-5kwVYW8ZIHo-unsplash-6f60f70759ac5b550768db73bb222080.jpg&amp;quot;);
    --photo-5: url(&amp;quot;static/joseph-barrientos-oQl0eVYd_n8-unsplash-3dd2ab53f69638b7f60592302dbefa66.jpg&amp;quot;);
    --photo-6: url(&amp;quot;static/matt-hardy-6ArTTluciuA-unsplash-69ba0671266d29e5626402d6afa7a5f8.jpg&amp;quot;);
    --photo-7: url(&amp;quot;static/shifaaz-shamoon-sLAk1guBG90-unsplash-47e91af7b69c20cdccc108cd931d8391.jpg&amp;quot;);
    --photo-8: url(&amp;quot;static/silas-baisch-K785Da4A_JA-unsplash-440d9ff1d9c7d8d0a20896dfdbf7b9c1.jpg&amp;quot;);
    --photo-9: url(&amp;quot;static/thomas-vimare-IZ01rjX0XQA-unsplash-2c59404afeaa4a7fa2bec02c3da439cf.jpg&amp;quot;);
  }
  html { overflow: hidden; }
  body {
    margin: 0;
    min-height: 100vh;
    background: #e9e0cb;
    font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  }

  form {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
  }

  form &amp;gt; button {
  grid-area: 1 / 1 / 2 / 2;
  align-self: center;
  justify-self: center;
  padding: 5px 10px;
  border-radius: 3px;
  border: none;
  background: #f9f0db;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  cursor: pointer;
  transition:
    scale 0.15s ease-in-out,
    box-shadow 0.15s ease-in-out;

  &amp;amp;:active {
    scale: 0.95;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
  }
}

  .polaroid .img {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    background: #121212;
  }

  .polaroid .img::before {
    content: &amp;#x27;&amp;#x27;;
    position: absolute;
    inset: 0;
    background: var(--photo);
    background-size: cover;
    background-position: center;
  }

  .polaroid:nth-last-child(1) { --photo: var(--photo-1); }
  .polaroid:nth-last-child(2) { --photo: var(--photo-2); }
  .polaroid:nth-last-child(3) { --photo: var(--photo-3); }
  .polaroid:nth-last-child(4) { --photo: var(--photo-4); }
  .polaroid:nth-last-child(5) { --photo: var(--photo-5); }
  .polaroid:nth-last-child(6) { --photo: var(--photo-6); }
  .polaroid:nth-last-child(7) { --photo: var(--photo-7); }
  .polaroid:nth-last-child(8) { --photo: var(--photo-8); }
  .polaroid:nth-last-child(9) { --photo: var(--photo-9); }


      .polaroid {
    grid-area: 1 / 1 / 2 / 2;
    position: relative;
    background: #f9f0db;
    border-radius: 3px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    width: min(400px, 82vw);
    aspect-ratio: 2.1 / 2.5;
    padding: 25px;
    transform:
      rotate(random(-15deg, 15deg))
      translate(random(-10px, 10px), random(-10px, 10px));
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form&amp;gt;
  &amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:344px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.polaroid {
    grid-area: 1 / 1 / 2 / 2;
    position: relative;
    background: #f9f0db;
    border-radius: 3px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    width: min(400px, 82vw);
    aspect-ratio: 2.1 / 2.5;
    padding: 25px;
    transform:
      rotate(random(-15deg, 15deg))
      translate(random(-10px, 10px), random(-10px, 10px));
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.polaroid {
    grid-area: 1 / 1 / 2 / 2;
    position: relative;
    background: #f9f0db;
    border-radius: 3px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    width: min(400px, 82vw);
    aspect-ratio: 2.1 / 2.5;
    padding: 25px;
    transform:
      rotate(random(-15deg, 15deg))
      translate(random(-10px, 10px), random(-10px, 10px));
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The transform here takes care of the entire effect: rotate it randomly between -15 and 15 degrees, and then translate it randomly by up to 10 pixels in both directions. This creates a nice &amp;quot;messy&amp;quot; stack of polaroids.&lt;/p&gt;&lt;h3 id=&quot;adding-interactivity&quot;&gt;Adding interactivity&lt;/h3&gt;&lt;p&gt;For the interactivity, we need to be able to set a state for each card when we click on it, and ideally we also want a way to reset the cards back to their original position.&lt;/p&gt;&lt;p&gt;Each card gets a checkbox that we overlay on top of the card:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroid&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;img&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroids/1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CSS to make sure the checkbox is invisible and covers the entire card:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.polaroid&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That gives us a &lt;code class=&quot;language-text&quot;&gt;:checked&lt;/code&gt; state for each card that we can use, but to uncheck it, we need to click each card separately again. If we want to be able to reset all cards at once, we&amp;#x27;re going to need use the platform.&lt;/p&gt;&lt;p&gt;By wrapping all the checkboxes in a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;form&amp;gt;&lt;/code&gt;, we can use the native &lt;code class=&quot;language-text&quot;&gt;reset&lt;/code&gt; behavior of the form to uncheck all the checkboxes at once when we click the reset button.&lt;/p&gt;&lt;p&gt;By placing the reset button &lt;em&gt;before&lt;/em&gt; the cards in the same grid stack, it will appear when the last polaroid is flung away:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;reset&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Stack them back up&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroid&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;img&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroids/1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroid&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;img&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;polaroids/2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--- etc --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;A note on accessibility:&lt;/strong&gt; This is not an accessible form and should not be used as-is in production. This is just a demo to show how you can use the &lt;code class=&quot;language-text&quot;&gt;:checked&lt;/code&gt; state of a checkbox and the reset feature of a form to trigger animations.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Now we can use the &lt;code class=&quot;language-text&quot;&gt;:checked&lt;/code&gt; state of each checkbox to trigger an animation that flings the card away in a random direction.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Polaroids: Click to Fling&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Polaroids: Click to Fling&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  
  * { box-sizing: border-box; }
  :root {
    --photo-1: url(&amp;quot;static/christoffer-engstrom-wc9avd2RaN0-unsplash-54d4c13a19c68cb52bce51077cf747e8.jpg&amp;quot;);
    --photo-2: url(&amp;quot;static/conor-sexton-hRemch0ZDwI-unsplash-882547c037e9a4523fa14c791d85f855.jpg&amp;quot;);
    --photo-3: url(&amp;quot;static/cristian-palmer-XexawgzYOBc-unsplash-a50326deaa4daf7df939b28145544f5d.jpg&amp;quot;);
    --photo-4: url(&amp;quot;static/giga-khurtsilava-5kwVYW8ZIHo-unsplash-6f60f70759ac5b550768db73bb222080.jpg&amp;quot;);
    --photo-5: url(&amp;quot;static/joseph-barrientos-oQl0eVYd_n8-unsplash-3dd2ab53f69638b7f60592302dbefa66.jpg&amp;quot;);
    --photo-6: url(&amp;quot;static/matt-hardy-6ArTTluciuA-unsplash-69ba0671266d29e5626402d6afa7a5f8.jpg&amp;quot;);
    --photo-7: url(&amp;quot;static/shifaaz-shamoon-sLAk1guBG90-unsplash-47e91af7b69c20cdccc108cd931d8391.jpg&amp;quot;);
    --photo-8: url(&amp;quot;static/silas-baisch-K785Da4A_JA-unsplash-440d9ff1d9c7d8d0a20896dfdbf7b9c1.jpg&amp;quot;);
    --photo-9: url(&amp;quot;static/thomas-vimare-IZ01rjX0XQA-unsplash-2c59404afeaa4a7fa2bec02c3da439cf.jpg&amp;quot;);
  }
  html { overflow: hidden; }
  body {
    margin: 0;
    min-height: 100vh;
    background: #e9e0cb;
    font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  }

  form {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
  }

  form &amp;gt; button {
  grid-area: 1 / 1 / 2 / 2;
  align-self: center;
  justify-self: center;
  padding: 5px 10px;
  border-radius: 3px;
  border: none;
  background: #f9f0db;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  cursor: pointer;
  transition:
    scale 0.15s ease-in-out,
    box-shadow 0.15s ease-in-out;

  &amp;amp;:active {
    scale: 0.95;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
  }
}

  .polaroid .img {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    background: #121212;
  }

  .polaroid .img::before {
    content: &amp;#x27;&amp;#x27;;
    position: absolute;
    inset: 0;
    background: var(--photo);
    background-size: cover;
    background-position: center;
  }

  .polaroid:nth-last-child(1) { --photo: var(--photo-1); }
  .polaroid:nth-last-child(2) { --photo: var(--photo-2); }
  .polaroid:nth-last-child(3) { --photo: var(--photo-3); }
  .polaroid:nth-last-child(4) { --photo: var(--photo-4); }
  .polaroid:nth-last-child(5) { --photo: var(--photo-5); }
  .polaroid:nth-last-child(6) { --photo: var(--photo-6); }
  .polaroid:nth-last-child(7) { --photo: var(--photo-7); }
  .polaroid:nth-last-child(8) { --photo: var(--photo-8); }
  .polaroid:nth-last-child(9) { --photo: var(--photo-9); }

  .polaroid {
    grid-area: 1 / 1 / 2 / 2;
    position: relative;
    background: #f9f0db;
    border-radius: 3px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    width: min(300px, 70vw);
    aspect-ratio: 2.1 / 2.5;
    padding: 15px;
    transform:
      rotate(random(-15deg, 15deg))
      translate(random(-10px, 10px), random(-10px, 10px));

    input {
      position: absolute;
      inset: 0;
      width:100%;
      height:100%;
      opacity: 0;
      cursor: pointer;
    }
  }
      

      .polaroid {
    transition: transform 0.33s ease-in-out;

    &amp;amp;:has(input:checked) {
      pointer-events:none;
      transform:
      translate(random(-50vw, 50vw), -70vh)
      rotate(random(-120deg, 120deg));
    }

    &amp;amp;:has(input:active) {
      transform: scale(1.2);
    }
  }
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;form&amp;gt;
  &amp;lt;button type=&amp;quot;reset&amp;quot;&amp;gt;Stack them back up&amp;lt;/button&amp;gt;
  &amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;polaroid&amp;quot;&amp;gt;
  &amp;lt;div class=&amp;quot;img&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:368px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.polaroid {
    transition: transform 0.33s ease-in-out;

    &amp;amp;:has(input:checked) {
      pointer-events:none;
      transform:
      translate(random(-50vw, 50vw), -70vh)
      rotate(random(-120deg, 120deg));
    }

    &amp;amp;:has(input:active) {
      transform: scale(1.2);
    }
  }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.polaroid {
    transition: transform 0.33s ease-in-out;

    &amp;amp;:has(input:checked) {
      pointer-events:none;
      transform:
      translate(random(-50vw, 50vw), -70vh)
      rotate(random(-120deg, 120deg));
    }

    &amp;amp;:has(input:active) {
      transform: scale(1.2);
    }
  }&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When a card is checked, it&amp;#x27;s translated to a random position near the top of the screen and rotated by a random amount to make it look like it&amp;#x27;s being thrown. As you click a card we use &lt;code class=&quot;language-text&quot;&gt;:has(input:active)&lt;/code&gt; to scale the card up slightly so it looks like you&amp;#x27;re picking it up, and then when you release the mouse button, it flies away and scales back down to its original size.&lt;/p&gt;&lt;p&gt;When we click the reset button, all checkboxes are unchecked and all cards return to their original position in the stack.&lt;/p&gt;&lt;h3 id=&quot;ordering&quot;&gt;Ordering&lt;/h3&gt;&lt;p&gt;Now a keen eye might see that each item on the stack is flung below the previous one, which is what you expect from the HTML structure, but isn&amp;#x27;t exactly how a real stack of polaroids would work.&lt;/p&gt;&lt;p&gt;In a real stack, the latest polaroid to flung away would land on top of the other ones, not below them.&lt;/p&gt;&lt;p&gt;We can fix that with some clever use of animation values to store the time you clicked each polaroid and append that to the z-index (so the last clicked is always highest), but we&amp;#x27;re going to leave that out-of-scope for this article.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/support/&quot;&gt;Let us know&lt;/a&gt; if you want to see a follow-up article where we go into how to do that!&lt;/p&gt;&lt;h2 id=&quot;designing-a-poem-with-random-item&quot;&gt;Designing a poem with &lt;code class=&quot;language-text&quot;&gt;random-item()&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;I had this idea of a poem where I would randomly start words on a new line or with different offsets, just like some visual poems do. Something like &lt;code class=&quot;language-text&quot;&gt;display: random-item(block, inline-block)&lt;/code&gt; would be perfect for that.&lt;/p&gt;&lt;p&gt;But &lt;code class=&quot;language-text&quot;&gt;random-items()&lt;/code&gt; doesn&amp;#x27;t exist yet.&lt;/p&gt;&lt;p&gt;As I was watching &lt;a href=&quot;https://www.kevinpowell.co/&quot;&gt;Kevin Powell&lt;/a&gt; at &lt;a href=&quot;https://cssday.nl/&quot;&gt;CSS Day&lt;/a&gt; show off how he uses &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; in CSS, I realised we could combine &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; with style queries in an &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; function to get a similar effect.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; the &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; function is not supported in Safari yet, so the demos below won&amp;#x27;t work there.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; gives you a value between two numbers, but if you want to get a random value like &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt;, you can&amp;#x27;t do that directly.&lt;/p&gt;&lt;p&gt;What you can do is use the &lt;code class=&quot;language-text&quot;&gt;step&lt;/code&gt; value in &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; to create an integer value between 1 and 0, and use that as a toggle in an &lt;code class=&quot;language-text&quot;&gt;if()&lt;/code&gt; function.&lt;/p&gt;&lt;h3 id=&quot;using-the-step-value-in-random&quot;&gt;Using the step value in &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;If you add a third value to the random function, it will only return values that are a multiple of that value.&lt;/p&gt;&lt;p&gt;If we set the step value to 1, it will only return whole numbers. If we also set the minimum to 0 and the maximum to 1, it will only return either 0 or 1.&lt;/p&gt;&lt;p&gt;Then we can use &lt;code class=&quot;language-text&quot;&gt;@property&lt;/code&gt; to cast that to an &lt;code class=&quot;language-text&quot;&gt;&amp;lt;integer&amp;gt;&lt;/code&gt;, and use that as a toggle in an if clause:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@property&lt;/span&gt; --discrete-number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;lt;integer&amp;gt;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inherits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; false&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;initial-value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Combining that, we can essentially recreate &lt;code class=&quot;language-text&quot;&gt;random-item()&lt;/code&gt;. For example, if we wanted to randomly pick a different petal for our falling petals demo instead of looping through them, it would look like this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@property&lt;/span&gt; --discrete-number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;syntax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;lt;integer&amp;gt;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inherits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; false&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;initial-value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.petal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/1.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/2.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/3.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;petals/4.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;#x27;s not too bad, is it!&lt;/p&gt;&lt;h3 id=&quot;styling-the-poem-with-discrete-random-values&quot;&gt;Styling the poem with discrete random values&lt;/h3&gt;&lt;p&gt;With this implemented I build a little demo of a poem where each word is randomly offset and randomly set to either &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt; to create a more interesting layout:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Poem: Discrete random() Toggle&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Poem: Discrete random() Toggle&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  * { box-sizing: border-box; }
  html { overflow: hidden; }
  body {
    margin: 0;
    min-height: 100vh;
    background: #e9e0cb;
    display: grid;
    place-items: center;
    padding: 2rem;
    font-family: &amp;quot;Courier Prime&amp;quot;, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, monospace;
    color: #181515;
  }

  .poem {
    width: min(42rem, 100%);
  }

  .poem span {
    font-size: 2rem;
  }


      @property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 1, 1);
  display: if(
    style(--discrete-number: 1): inline-block;
    else: block;
  );
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;poem&amp;quot;&amp;gt;&amp;lt;span&amp;gt;The&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;serpent&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;stared&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;in&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;surprise&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;at&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;this&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;interloper.&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;but&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;God&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;said:&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;&amp;quot;You&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;see&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;this&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;apple?&amp;quot;&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;I&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;squeeze&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;it&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;and&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;look-cider.&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:344px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;@property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 1, 1);
  display: if(
    style(--discrete-number: 1): inline-block;
    else: block;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;@property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 1, 1);
  display: if(
    style(--discrete-number: 1): inline-block;
    else: block;
  );
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Refreshing that demo a bunch of time I realised that with this discrete number, I had a 50/50 chance of a word being on a new line or not. That didn&amp;#x27;t look great.&lt;/p&gt;&lt;p&gt;I wanted to have more control over those odds, i wanted to be able to say &amp;quot;I want 70% of the words to be &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt; and only 30% to start on a new line&amp;quot;.&lt;/p&gt;&lt;h3 id=&quot;weighted-random-values&quot;&gt;Weighted random values&lt;/h3&gt;&lt;p&gt;By increasing the max value to 10 and then using a style query that checks if the value is above or below 3, we can achieve that:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--discrete-number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 10&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--discrete-number &amp;gt; 3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Currently, the proposal for &lt;code class=&quot;language-text&quot;&gt;random-item()&lt;/code&gt; does not include a way to weight the odds of each item being selected. That would be very useful though, so I opened an issue on the &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/14079&quot;&gt;CSSWG repo&lt;/a&gt;. If you want to see that feature implemented, please go and comment on that issue.&lt;/p&gt;&lt;h3 id=&quot;a-visual-poem&quot;&gt;A visual poem&lt;/h3&gt;&lt;p&gt;Using that weighed value we can set a number of options based on that single discrete number and use different weighting for each value.&lt;/p&gt;&lt;p&gt;This ended up being a fun way to create a visual poem where each refresh puts the emphasis on different aspects of the same poem:&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Poem: Weighted Visual Layout&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Poem: Weighted Visual Layout&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
  * { box-sizing: border-box; }
  html { overflow: hidden; }
  body {
    margin: 0;
    min-height: 100vh;
    background: #e9e0cb;
    display: grid;
    place-items: center;
    padding: 2rem;
    font-family: &amp;quot;Courier Prime&amp;quot;, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, &amp;quot;Liberation Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, monospace;
    color: #181515;
  }

  .poem {
    width: min(42rem, 100%);
  }

  .poem span {
    font-size: 2rem;
  }


      @property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 10, 1);
  display: if(
    style(--discrete-number &amp;gt; 2): inline-block;
    else: block;
  );
  margin-left: if(
    style(--discrete-number &amp;lt; 2): random(0rem, 6rem);
    else: 0;
  );
  font-weight: if(
    style(--discrete-number &amp;gt; 5): random(400, 800);
    else: 400;
  );
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;poem&amp;quot;&amp;gt;&amp;lt;span&amp;gt;The&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;serpent&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;stared&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;in&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;surprise&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;at&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;this&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;interloper.&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;but&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;God&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;said:&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;&amp;quot;You&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;see&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;this&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;apple?&amp;quot;&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;I&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;squeeze&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;it&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;and&amp;lt;/span&amp;gt; &amp;lt;span&amp;gt;look-cider.&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:536px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;@property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 10, 1);
  display: if(
    style(--discrete-number &amp;gt; 2): inline-block;
    else: block;
  );
  margin-left: if(
    style(--discrete-number &amp;lt; 2): random(0rem, 6rem);
    else: 0;
  );
  font-weight: if(
    style(--discrete-number &amp;gt; 5): random(400, 800);
    else: 400;
  );
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;@property --discrete-number {
  syntax: &amp;quot;&amp;lt;integer&amp;gt;&amp;quot;;
  inherits: false;
  initial-value: 0;
}

.poem span {
  --discrete-number: random(0, 10, 1);
  display: if(
    style(--discrete-number &amp;gt; 2): inline-block;
    else: block;
  );
  margin-left: if(
    style(--discrete-number &amp;lt; 2): random(0rem, 6rem);
    else: 0;
  );
  font-weight: if(
    style(--discrete-number &amp;gt; 5): random(400, 800);
    else: 400;
  );
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;We can even use random &lt;em&gt;inside&lt;/em&gt; the if clause, so there is a higher chance of a word being on a new line, but if it is on a new line, it also has a random chance of being offset by a random amount.&lt;/p&gt;&lt;h2 id=&quot;more-demos&quot;&gt;More demos&lt;/h2&gt;&lt;p&gt;I have a few more demos that I created but this article is already quite long and covered &lt;em&gt;most&lt;/em&gt; of what I wanted to show, so I&amp;#x27;ll just show the additional demos below.&lt;/p&gt;&lt;p&gt;Make sure to also check out &lt;a href=&quot;https://alvaromontoro.com/blog/68092/native-random-values-in-css&quot;&gt;Randomness in CSS&lt;/a&gt; by Alvaro Montoro. Some of the terminology has already changed since that article, but it takes a more descriptive approach at introducing the new function that you might like.&lt;/p&gt;&lt;h3 id=&quot;shaky-grid&quot;&gt;Shaky Grid&lt;/h3&gt;&lt;p&gt;No animation here, but I thought it would be fun to create a shaky grid pattern where each line is slightly offset and rotated.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Shaky Grid&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm &quot; aria-hidden=&quot;false&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewCanvas--2mRvo&quot; style=&quot;transform:scale(min(1, calc(100cqw / 1200px)))&quot;&gt;&lt;iframe title=&quot;Shaky Grid&quot; srcDoc=&quot;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot; /&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot; /&amp;gt;
    &amp;lt;style&amp;gt;
      * { box-sizing: border-box; }
    html { overflow: hidden; }
    body {margin: 0;}

      
        * { box-sizing: border-box; }
        body {
          position: absolute;
          margin: 0;
          inset: 0;
          background: #e9e0cb;
          overflow: hidden;
          display: flex;
          align-items: stretch;
          justify-content: flex-start;
        }
          .vertical {
  display: flex;
  position: absolute;
  inset: 0;
  overflow: clip;
  align-items: stretch;
  justify-content: space-around;
}

.horizontal {
  display: flex;
  position: absolute;
  flex-direction: column;
  inset: 0;
  overflow: clip;
  align-items: stretch;
  justify-content: space-around;
}

.line {width:100%;
  background: #000a;}
      

      .line {
  transform:
    scale(1.5)
    rotate(random(-10deg, 10deg))
    skew(random(-10deg, 10deg), random(-10deg, 10deg));
}
.vertical .line {
  width: random(5px, 10px);
}

.horizontal .line {
  height: random(5px, 10px);
}
    &amp;lt;/style&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;vertical&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;horizontal&amp;quot;&amp;gt;&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;line&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:344px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.line {
  transform:
    scale(1.5)
    rotate(random(-10deg, 10deg))
    skew(random(-10deg, 10deg), random(-10deg, 10deg));
}
.vertical .line {
  width: random(5px, 10px);
}

.horizontal .line {
  height: random(5px, 10px);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.line {
  transform:
    scale(1.5)
    rotate(random(-10deg, 10deg))
    skew(random(-10deg, 10deg), random(-10deg, 10deg));
}
.vertical .line {
  width: random(5px, 10px);
}

.horizontal .line {
  height: random(5px, 10px);
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;randomly-rotating-rectangles&quot;&gt;Randomly rotating rectangles&lt;/h3&gt;&lt;p&gt;This was sort of a step-up to the petal example. Each rectangle sits inside a grid area and is randomly rotated and offset. The animation is continuous.&lt;/p&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Randomly Rotating Rectangles&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:464px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.tile {
  width: random(25%, 75%);
  height: random(25%, 75%);
  border-radius: random(10px, 20px);
  transform-style: preserve-3d;
  --rotx: random(-25deg, 25deg);
  animation: rotate random(5s, 10s) linear infinite;
  animation-delay: random(-5s, 0s);
}

@keyframes rotate {
  from {
    transform: perspective(500px)rotateY(0deg) rotateZ(-30deg);
  }
  to {
    transform: perspective(500px) rotateY(360deg) rotateZ(330deg);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt;.tile {
  width: random(25%, 75%);
  height: random(25%, 75%);
  border-radius: random(10px, 20px);
  transform-style: preserve-3d;
  --rotx: random(-25deg, 25deg);
  animation: rotate random(5s, 10s) linear infinite;
  animation-delay: random(-5s, 0s);
}

@keyframes rotate {
  from {
    transform: perspective(500px)rotateY(0deg) rotateZ(-30deg);
  }
  to {
    transform: perspective(500px) rotateY(360deg) rotateZ(330deg);
  }
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;aurora&quot;&gt;Aurora&lt;/h3&gt;&lt;p&gt;This one is quite interesting because it uses a few &lt;code class=&quot;language-text&quot;&gt;@property&lt;/code&gt; declarations to make things like the length portion of a linear gradient offset configurable.&lt;/p&gt;&lt;p&gt;It is however very similar to the bokeh example in terms of how the style is achieved, and it&amp;#x27;s also quite a bit more resource intensive. Dig in and play around with it, but be aware that it might be a bit heavy on your CPU.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; This demo doesn&amp;#x27;t work well in Safari, presumably because it&amp;#x27;s using &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; inside animations.&lt;/p&gt;&lt;/blockquote&gt;&lt;div class=&quot;RandomCssDemo-styles-module--demo--2igtN&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBar--39md4&quot;&gt;&lt;h4 class=&quot;RandomCssDemo-styles-module--title--1lKp0&quot;&gt;Aurora&lt;/h4&gt;&lt;div class=&quot;RandomCssDemo-styles-module--topBarActions--Q3cKK&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--randomButton--B7uZm RandomCssDemo-styles-module--randomButtonHidden--MntYB&quot; aria-hidden=&quot;true&quot; tabindex=&quot;-1&quot;&gt;&lt;svg class=&quot;RandomCssDemo-styles-module--randomIcon--3Hi9w&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Randomize&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--layout--1evq8&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewPane--3ifED&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--previewFrame--1XHVR&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--showButton--oW-a3&quot;&gt;Show Demo&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codePane--2BykC&quot;&gt;&lt;div class=&quot;RandomCssDemo-styles-module--codeBlock--1DSBj&quot; style=&quot;--code-height:1376px&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;RandomCssDemo-styles-module--copyButton--2J2ts&quot;&gt;copy code&lt;/button&gt;&lt;div class=&quot;RandomCssDemo-styles-module--highlightScroll--2KrHS&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt; @property --skew {
  syntax: &amp;quot;&amp;lt;angle&amp;gt;&amp;quot;;
  inherits: true;
  initial-value: 0deg;
}

@property --length {
  syntax: &amp;quot;&amp;lt;percentage&amp;gt;&amp;quot;;
  inherits: true;
  initial-value: 100%;
}

.beam {
  background: linear-gradient(
    to bottom,
    hsl(random(100, 280) 100% 50%),
    hsl(random(100, 280) 50% 0%) var(--length)
  );

  mix-blend-mode: plus-lighter;
  filter: blur(8px);
  animation:
    rotate random(3s, 8s) ease-in-out infinite alternate,
    grow random(2s, 11s) ease-in-out infinite alternate,
    move random(8s, 12s) ease-in-out infinite alternate;
  animation-delay: random(-5s, 0s), random(-5s, 0s), random(-8s, 0s);
  transform: perspective(300px) rotateX(20deg) skewX(var(--skew));

}

@keyframes rotate {
  from {
    --skew: random(10deg, 14deg);
  }
  to {
    --skew: random(12deg, 18deg);
  }
}

@keyframes grow {
  from {
    --length: random(20%, 50%);
  }
  to {
    --length: random(40%, 60%);
  }
}

@keyframes move {
  from {
    translate: random(-2vw, 0vw) -10%;
  }
  to {
    translate: random(0vw, 2vw) -10%;
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;textarea class=&quot;RandomCssDemo-styles-module--codeEditor--22RYG&quot; spellcheck=&quot;false&quot; wrap=&quot;soft&quot;&gt; @property --skew {
  syntax: &amp;quot;&amp;lt;angle&amp;gt;&amp;quot;;
  inherits: true;
  initial-value: 0deg;
}

@property --length {
  syntax: &amp;quot;&amp;lt;percentage&amp;gt;&amp;quot;;
  inherits: true;
  initial-value: 100%;
}

.beam {
  background: linear-gradient(
    to bottom,
    hsl(random(100, 280) 100% 50%),
    hsl(random(100, 280) 50% 0%) var(--length)
  );

  mix-blend-mode: plus-lighter;
  filter: blur(8px);
  animation:
    rotate random(3s, 8s) ease-in-out infinite alternate,
    grow random(2s, 11s) ease-in-out infinite alternate,
    move random(8s, 12s) ease-in-out infinite alternate;
  animation-delay: random(-5s, 0s), random(-5s, 0s), random(-8s, 0s);
  transform: perspective(300px) rotateX(20deg) skewX(var(--skew));

}

@keyframes rotate {
  from {
    --skew: random(10deg, 14deg);
  }
  to {
    --skew: random(12deg, 18deg);
  }
}

@keyframes grow {
  from {
    --length: random(20%, 50%);
  }
  to {
    --length: random(40%, 60%);
  }
}

@keyframes move {
  from {
    translate: random(-2vw, 0vw) -10%;
  }
  to {
    translate: random(0vw, 2vw) -10%;
  }
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;a-work-in-progress&quot;&gt;A work in progress&lt;/h2&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; in CSS is still very much a work-in-progress. Support across browsers is patchy, the specification is still evolving and there are quite a few bugs (that hopefully I&amp;#x27;ve sneakily worked around in the examples above, while also reporting reduced test cases). Still, having the ability to use randomness in CSS opens up so much new possibilities for creative designs and effects, and i&amp;#x27;m really only scratching the surface of what can be done with it.&lt;/p&gt;&lt;p&gt;There are a number of things you can do with &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt; that I left out:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;using custom identifiers to create a random value that is the same across multiple elements&lt;/li&gt;&lt;li&gt;setting different scopes&lt;/li&gt;&lt;li&gt;forcing fixed seed values&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Things that I simply didn&amp;#x27;t find an immediate use-case for (or, to be honest, fully understand).&lt;/p&gt;&lt;p&gt;If you have an idea for a cool use-case for any of those features, or if you want to see a follow-up article where we go into some of those more advanced features, please &lt;a href=&quot;/support/&quot;&gt;let us know&lt;/a&gt;!&lt;/p&gt;&lt;p&gt;If, like me, you simply want to have a go at some fun and creative experiments with &lt;code class=&quot;language-text&quot;&gt;random()&lt;/code&gt;, you can &lt;a href=&quot;https://dashboard.polypane.app/register/&quot;&gt;get Polypane&lt;/a&gt; and play around with it before it lands in normal browsers.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using safe-area-inset to build mobile-safe layouts]]></title><description><![CDATA[Modern phones are not simple rectangles. They have rounded corners, camera cutouts, dynamic islands, and home indicators that double as…]]></description><link>https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/</link><guid isPermaLink="false">https://polypane.app/blog/using-safe-area-inset-to-build-mobile-safe-layouts/</guid><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern phones are not simple rectangles. They have rounded corners, camera cutouts, dynamic islands, and home indicators that double as gesture areas. Browsers know the dimensions of all of these and expose the parts that could obscure content as &lt;strong&gt;safe area insets&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;The &amp;quot;safe area&amp;quot; is the portion of the screen that is guaranteed to be free from being obscured by system UI. The safe-area-inset is the measurement of how much space the system UI is taking up on each edge of the screen. By using these values in your CSS, you can make sure that important content and controls are not obscured by the system UI.&lt;/p&gt;&lt;p&gt;If you don&amp;#x27;t want your floating chat button to end up sitting behind the home indicator, where it&amp;#x27;s unreachable, you need to account for the safe area inset.&lt;/p&gt;&lt;h2 id=&quot;environment-variables-for-safe-area-insets&quot;&gt;Environment variables for safe area insets&lt;/h2&gt;&lt;p&gt;With the safe-area-inset environment variables, you can make your layout adapt to the current device&amp;#x27;s safe area and avoid those bugs. The &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; function is how you read those values in CSS:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-top&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-left&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;browser-support&quot;&gt;Browser support&lt;/h3&gt;&lt;p&gt;Safe-area-insets are &lt;strong&gt;baseline widely available&lt;/strong&gt;, which means you can use them in production today and be confident that they will work for almost all your users on mobile devices.&lt;/p&gt;&lt;p&gt;Since it&amp;#x27;s baseline widely available, you don&amp;#x27;t &lt;em&gt;really&lt;/em&gt; need to think about fallbacks but if you want to be extra safe, you can provide a fallback padding if the browser doesn&amp;#x27;t support &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Fallback for browsers that don&amp;#x27;t support env() */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-top&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And for browsers that support &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; but not &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-*&lt;/code&gt; variables, you can provide a fallback value directly in the &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; function. If &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-top&lt;/code&gt; is not supported, it will fall back to &lt;code class=&quot;language-text&quot;&gt;1rem&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-top&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;#x27;s worth noting that this situation is purely theoretical, as all browsers that support &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; also support &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-*&lt;/code&gt; variables. That might not be the case with other environment variables, so it&amp;#x27;s good to know that this fallback mechanism exists.&lt;/p&gt;&lt;h2 id=&quot;using-safe-area-inset-values&quot;&gt;Using safe-area-inset values&lt;/h2&gt;&lt;p&gt;Before we get into problem areas, let&amp;#x27;s get a general understanding of how these variables work and how you can use them to affect the layout.&lt;/p&gt;&lt;p&gt;In this demo, change each inset and watch how a realistic mobile UI that takes safe area insets into account shifts to remain usable:&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--3CX34&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbar--1dps_&quot;&gt;&lt;span class=&quot;examples-styles-module--playgroundToolbarLeft--1oxJJ&quot;&gt;Cutout type&lt;/span&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbarRight--1AqsU&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--rotateButton--3GJfI&quot;&gt;&lt;svg class=&quot;examples-styles-module--rotateIcon--281eZ&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Rotate&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundLayout--27-kJ&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundControls--2hFRj&quot;&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;notch&quot; selected=&quot;&quot;&gt;notch&lt;/option&gt;&lt;option value=&quot;island&quot;&gt;dynamic island&lt;/option&gt;&lt;option value=&quot;punch&quot;&gt;camera punch hole&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Top inset (44px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;70&quot; value=&quot;44&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Right inset (0px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;40&quot; value=&quot;0&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Bottom inset (34px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;60&quot; value=&quot;34&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Left inset (0px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;40&quot; value=&quot;0&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Overlay&lt;/label&gt;&lt;label class=&quot;examples-styles-module--overlayToggle--1GwPi&quot;&gt;&lt;input type=&quot;checkbox&quot; checked=&quot;&quot;/&gt;Show safe inset areas&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundPhoneCol--2_Oxe&quot; style=&quot;cursor:grab;touch-action:none;user-select:none&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 &quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY &quot; style=&quot;--safe-top:44px;--safe-right:0px;--safe-bottom:34px;--safe-left:0px;--safe-max-bottom:34px;--browser-bar-size:0px;--chat-offset:10px;--action-bar-size:34px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot;&gt;Page title&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--footerButton--159Wi&quot;&gt;Safe action area&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlay--3hWNq&quot;&gt;&lt;div class=&quot;examples-styles-module--overlayTop--149h6&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayRight--3O4sQ&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayBottom--14Iqa&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayLeft--3Mf46&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg &quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutNotch--Pot4c&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The specific px values here are not particular to any specific device, the point is to show how the device sets these variables and how your layout can respond to them as they change.&lt;/p&gt;&lt;p&gt;On real devices, you can think of these values as constants. They&amp;#x27;re provided by the browser and while they might change when switching from portrait to landscape or between OS updates that change the device UI as well as simply differ per device, they don&amp;#x27;t change dynamically as the user scrolls or interacts with the page. The browser provides them as a constant value that you can use to ensure your content is not obscured by the system UI.&lt;/p&gt;&lt;h2 id=&quot;which-web-pages-actually-need-this&quot;&gt;Which web pages actually need this?&lt;/h2&gt;&lt;p&gt;If you want your pages to look the best they can, all of them.&lt;/p&gt;&lt;p&gt;Browsers by default will prevent your site from being obscured by the notch or home indicator, so your content will be safe without any special handling. That does come with a downside, which is that the browser will give you a smaller viewport to reserve space:&lt;/p&gt;&lt;div style=&quot;margin:2rem 0&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 examples-styles-module--frameViewportRotated--1RgPW&quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY examples-styles-module--frameRotated--1dJZv&quot; style=&quot;--safe-top:0px;--safe-right:0px;--safe-bottom:18px;--safe-left:0px;--safe-max-bottom:18px;--browser-bar-size:0px;--chat-offset:10px;--action-bar-size:18px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot; style=&quot;margin:0 44px&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot; style=&quot;padding-top:8px;height:42px&quot;&gt;Page title&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot; style=&quot;overflow:auto&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--footerButton--159Wi&quot;&gt;Safe action area&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg examples-styles-module--deviceChromeRotated--P0p4E&quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutIsland--33g48&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;You&amp;#x27;ll notice the edges here keep the site from being obscured, but they also don&amp;#x27;t look that great. Ideally we want the content to stretch edge-to-edge, but we want to make sure they&amp;#x27;re not obscured by system UI. To get that, you need to opt in to the full viewport and handle safe areas yourself.&lt;/p&gt;&lt;p&gt;To do so is a two-step process. First, you need to add &lt;code class=&quot;language-text&quot;&gt;viewport-fit=cover&lt;/code&gt; to your meta viewport tag:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;width=device-width, viewport-fit=cover&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will make the browser stretch your page edge-to-edge:&lt;/p&gt;&lt;div style=&quot;margin:2rem 0&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 examples-styles-module--frameViewportRotated--1RgPW&quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY examples-styles-module--frameRotated--1dJZv&quot; style=&quot;--safe-top:0px;--safe-right:0px;--safe-bottom:0px;--safe-left:0px;--safe-max-bottom:0px;--browser-bar-size:0px;--chat-offset:10px;--action-bar-size:18px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot; style=&quot;padding-top:8px;height:42px&quot;&gt;Page title&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot; style=&quot;overflow:auto&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--footerButton--159Wi&quot;&gt;Safe action area&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg examples-styles-module--deviceChromeRotated--P0p4E&quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutIsland--33g48&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;As you can see, the content sits behind the dynamic island and home indicator. &lt;code class=&quot;language-text&quot;&gt;viewport-fit=cover&lt;/code&gt; tells the browser that you want to be responsible for making sure your content is not obscured by the system UI.&lt;/p&gt;&lt;p&gt;And now we can move those elements away from behind the system UI using &lt;code class=&quot;language-text&quot;&gt;env(safe-area-inset-*)&lt;/code&gt;.&lt;/p&gt;&lt;div style=&quot;margin:2rem 0&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 examples-styles-module--frameViewportRotated--1RgPW&quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY examples-styles-module--frameRotated--1dJZv&quot; style=&quot;--safe-top:0px;--safe-right:44px;--safe-bottom:18px;--safe-left:44px;--safe-max-bottom:18px;--browser-bar-size:0px;--chat-offset:10px;--action-bar-size:18px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot;&gt;Page title&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot; style=&quot;overflow:auto&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--footerButton--159Wi&quot;&gt;Safe action area&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg examples-styles-module--deviceChromeRotated--P0p4E&quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutIsland--33g48&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-left&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you opt into the full viewport, here are the kinds of things you now have to think about to make sure your content is not obscured by the system UI:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Fixed headers and navigation bars should have enough space above or below for the notch/dynamic island/camera punch hole or home indicator&lt;/li&gt;&lt;li&gt;Floating chat or help buttons should remain inside the safe area&lt;/li&gt;&lt;li&gt;Full-screen dialogs and drawers should take up the right height without being obscured by the home indicator&lt;/li&gt;&lt;li&gt;Map or video controls near screen corners&lt;/li&gt;&lt;/ul&gt;&lt;h3 id=&quot;safe-area-inset-doesnt-provide-margins&quot;&gt;Safe-area-inset doesn&amp;#x27;t provide margins&lt;/h3&gt;&lt;p&gt;Safe-area-insets are defined to be exactly the space that the system UI is taking up. That means they don&amp;#x27;t provide any margin between the system UI&amp;#x27;s edge and your content.&lt;/p&gt;&lt;p&gt;If you set padding to just the safe-area-inset value, your content will sit right up against the edge of the safe area, which is right up against the system UI.&lt;/p&gt;&lt;p&gt;To add some breathing room, you can add your own padding on top of the safe area insets by combining things with &lt;code class=&quot;language-text&quot;&gt;calc()&lt;/code&gt;. This way you can ensure that your content is not only safe from being obscured but also has some space to breathe:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-top&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-left&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;safe-area-inset-only-has-non-zero-values-on-mobile-devices&quot;&gt;Safe-area-inset only has non-zero values on mobile devices&lt;/h2&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; is supported across browsers and platforms, but the &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-*&lt;/code&gt; variables really only have non-zero values on mobile devices.&lt;/p&gt;&lt;p&gt;Desktop browsers always return 0 because there is no UI on top of pages in desktop browsers. It&amp;#x27;s only on mobile devices that these values are non-zero and that you have to account for them.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s exactly why these bugs are so easy to miss. If you test in the Chrome responsive view, the safe area insets will still be 0 and you won&amp;#x27;t see any issues during development.&lt;/p&gt;&lt;p&gt;Testing on real devices is often delegated to the end of the project. By then, making layout changes to accommodate safe areas can be expensive, and the bugs can slip through to production. Even worse, they often only affect users on certain devices, so they can go unnoticed for a long time.&lt;/p&gt;&lt;h2 id=&quot;polypanes-device-emulation-supports-safe-area-insets&quot;&gt;Polypane&amp;#x27;s device emulation supports safe area insets&lt;/h2&gt;&lt;p&gt;Polypane is the first and only desktop browser to emulate safe area insets. Every &lt;a href=&quot;/docs/device-presets/&quot;&gt;device&lt;/a&gt; in Polypane&amp;#x27;s &lt;a href=&quot;/docs/emulation/&quot;&gt;device emulation&lt;/a&gt; has correct safe area inset values for both portrait and landscape orientations.&lt;/p&gt;&lt;p&gt;We added this as part of &lt;a href=&quot;/blog/polypane-26-accurate-device-emulation-with-safe-area-and-small-viewport-units/&quot;&gt;Polypane 26&lt;/a&gt;, along with emulation for small viewport units, another common source of mobile layout bugs.&lt;/p&gt;&lt;p&gt;Here&amp;#x27;s Polypane showing the safe area insets in blue, and the small viewport height difference in pink:&lt;/p&gt;&lt;img src=&quot;static/insetviz-be77ee8c0ab29ccda899743cd9a9b339.png&quot; alt=&quot;a device showing safe area and small viewport overlays in Polypane&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;&lt;em&gt;We&amp;#x27;re also the only desktop browser to emulate &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt;, but that&amp;#x27;s a topic for another article.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;With Polypane&amp;#x27;s safe-area-inset &lt;strong&gt;overlay visualization&lt;/strong&gt;, you can see exactly where the unsafe areas are on each device.&lt;/p&gt;&lt;h2 id=&quot;solving-a-specific-issue-floating-buttons&quot;&gt;Solving a specific issue: Floating buttons&lt;/h2&gt;&lt;p&gt;Let&amp;#x27;s look at a specific example of how to use &lt;code class=&quot;language-text&quot;&gt;safe-area-inset&lt;/code&gt; values to solve a common issue: floating buttons that end up behind the home indicator and become unreachable.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--3CX34&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbar--1dps_&quot;&gt;&lt;span class=&quot;examples-styles-module--playgroundToolbarLeft--1oxJJ&quot;&gt;Cutout type&lt;/span&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbarRight--1AqsU&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--rotateButton--3GJfI&quot;&gt;&lt;svg class=&quot;examples-styles-module--rotateIcon--281eZ&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Rotate&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundLayout--27-kJ&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundControls--2hFRj&quot;&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;notch&quot; selected=&quot;&quot;&gt;notch&lt;/option&gt;&lt;option value=&quot;island&quot;&gt;dynamic island&lt;/option&gt;&lt;option value=&quot;punch&quot;&gt;camera punch hole&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Bottom inset (34px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;70&quot; value=&quot;34&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Right inset (0px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;40&quot; value=&quot;0&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Button offset (10px)&lt;/label&gt;&lt;input type=&quot;range&quot; min=&quot;0&quot; max=&quot;40&quot; value=&quot;10&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Overlay&lt;/label&gt;&lt;label class=&quot;examples-styles-module--overlayToggle--1GwPi&quot;&gt;&lt;input type=&quot;checkbox&quot; checked=&quot;&quot;/&gt;Show safe inset areas&lt;/label&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Positioning&lt;/label&gt;&lt;div class=&quot;examples-styles-module--segmentedToggle--1ikOh&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--segmentedToggleBtn--1BZG8 examples-styles-module--segmentedToggleBtnBuggy--3nEeO examples-styles-module--segmentedToggleBtnActive--Jdsj3&quot;&gt;Fixed position&lt;/button&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--segmentedToggleBtn--1BZG8 examples-styles-module--segmentedToggleBtnSafe--3QVlg &quot;&gt;Using env()&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundPhoneCol--2_Oxe&quot; style=&quot;cursor:grab;touch-action:none;user-select:none&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 &quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY &quot; style=&quot;--safe-top:44px;--safe-right:0px;--safe-bottom:34px;--safe-left:0px;--safe-max-bottom:34px;--browser-bar-size:0px;--chat-offset:10px;--action-bar-size:34px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot;&gt;Bottom-right helper&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--chatButtonBad--3jQqG&quot;&gt;Chat&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlay--3hWNq&quot;&gt;&lt;div class=&quot;examples-styles-module--overlayTop--149h6&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayRight--3O4sQ&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayBottom--14Iqa&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayLeft--3Mf46&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg &quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutNotch--Pot4c&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--3344v&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.chat-button {
  position: fixed;
  right: 10px;
  bottom: 10px;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Toggle the positioning between &amp;#x27;Fixed position&amp;#x27; and &amp;#x27;Using env()&amp;#x27; to switch between hard-coded offsets and offsets that use &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-bottom&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-right&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;safe-area-max-inset&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Along with &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-*&lt;/code&gt;, the specification also describes &lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset-*&lt;/code&gt; variables. Those are not widely supported yet, but they are worth mentioning because they have slightly different behavior:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;safe-area-inset-*&lt;/code&gt;&lt;/strong&gt; gives you the &lt;em&gt;current&lt;/em&gt; inset value right now. On scroll, the browser chrome can collapse, and the inset value can shrink all the way to &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;. Your element moves with that change.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset-*&lt;/code&gt;&lt;/strong&gt; gives you the &lt;em&gt;maximum&lt;/em&gt; inset the browser can report for that edge. It stays stable even when the browser chrome collapses. Use it when you want a reserved zone that does not jump around.&lt;/li&gt;&lt;/ul&gt;&lt;div class=&quot;examples-styles-module--demo--3CX34&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbar--1dps_&quot;&gt;&lt;span class=&quot;examples-styles-module--playgroundToolbarLeft--1oxJJ&quot;&gt;inset vs max-inset&lt;/span&gt;&lt;div class=&quot;examples-styles-module--playgroundToolbarRight--1AqsU&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--rotateButton--3GJfI&quot;&gt;&lt;svg class=&quot;examples-styles-module--rotateIcon--281eZ&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2.2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot;&gt;&lt;path d=&quot;M23 4v6h-6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M1 20v-6h6&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;Rotate&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundLayout--27-kJ&quot;&gt;&lt;div class=&quot;examples-styles-module--playgroundControls--2hFRj&quot;&gt;&lt;p class=&quot;examples-styles-module--hint--34Pn9&quot;&gt;Drag or scroll the phone to simulate the browser address bar appearing and disappearing.&lt;/p&gt;&lt;p class=&quot;examples-styles-module--hint--34Pn9&quot;&gt;The &lt;strong style=&quot;color:#05a179&quot;&gt;green button&lt;/strong&gt; uses &lt;code&gt;safe-area-inset-bottom&lt;/code&gt; and follows the current inset. The &lt;strong style=&quot;color:#0f63d8&quot;&gt;blue button&lt;/strong&gt; uses &lt;code&gt;safe-area-max-inset-bottom&lt;/code&gt; and stays put.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--field--KN6R7&quot;&gt;&lt;label&gt;Overlay&lt;/label&gt;&lt;label class=&quot;examples-styles-module--overlayToggle--1GwPi&quot;&gt;&lt;input type=&quot;checkbox&quot; checked=&quot;&quot;/&gt;Show safe inset areas&lt;/label&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--playgroundPhoneCol--2_Oxe&quot; style=&quot;cursor:grab;touch-action:none;user-select:none&quot;&gt;&lt;div class=&quot;examples-styles-module--frameWrap--3SA8S&quot;&gt;&lt;div class=&quot;examples-styles-module--frameViewport--2C9A7 &quot;&gt;&lt;div class=&quot;examples-styles-module--frame--2u1NY &quot; style=&quot;--safe-top:32px;--safe-right:0px;--safe-bottom:54px;--safe-left:0px;--safe-max-bottom:54px;--browser-bar-size:36px;--chat-offset:10px;--action-bar-size:18px&quot;&gt;&lt;div class=&quot;examples-styles-module--screen--2E1fH&quot;&gt;&lt;div class=&quot;examples-styles-module--topBar--3AWE1&quot;&gt;inset vs max-inset&lt;/div&gt;&lt;div class=&quot;examples-styles-module--content--3ismc&quot;&gt;&lt;div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-star&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M16 2 L20 12 30 12 22 19 25 30 16 23 7 30 10 19 2 12 12 12 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bell&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M8 17 C8 12 9 6 16 6 23 6 24 12 24 17 24 22 27 25 27 25 L5 25 C5 25 8 22 8 17 Z M20 25 C20 25 20 29 16 29 12 29 12 25 12 25 M16 3 L16 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-mail&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 26 L30 26 30 6 2 6 Z M2 6 L16 16 30 6&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-bookmark&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M6 2 L26 2 26 30 16 20 6 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-calendar&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 6 L2 30 30 30 30 6 Z M2 15 L30 15 M7 3 L7 9 M13 3 L13 9 M19 3 L19 9 M25 3 L25 9&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-photo&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M20 24 L12 16 2 26 2 2 30 2 30 24 M16 20 L22 14 30 22 30 30 2 30 2 24&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;10&quot; cy=&quot;9&quot; r=&quot;3&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-user&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M22 11 C22 16 19 20 16 20 13 20 10 16 10 11 10 6 12 3 16 3 20 3 22 6 22 11 Z M4 30 L28 30 C28 21 22 20 16 20 10 20 4 21 4 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-clock&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;14&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M16 8 L16 16 20 20&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-activity&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 16 L11 16 14 29 18 3 21 16 28 16&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-archive&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M4 10 L4 28 28 28 28 10 M2 4 L2 10 30 10 30 4 Z M12 15 L20 15&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-camera&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M2 8 L 9 8 12 4 20 4 23 8 30 8 30 26 2 26 Z&quot;&gt;&lt;/path&gt;&lt;circle cx=&quot;16&quot; cy=&quot;16&quot; r=&quot;5&quot;&gt;&lt;/circle&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-fire&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M15 30 C0 30 15 13 12 3 28 13 32 30 15 30 Z M17 30 C10 30 17 20 17 20 17 20 24 30 17 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-location&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;16&quot; cy=&quot;11&quot; r=&quot;4&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M24 15 C21 22 16 30 16 30 16 30 11 22 8 15 5 8 10 2 16 2 22 2 27 8 24 15 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-music&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;path d=&quot;M11 25 L11 6 24 3 24 23 M11 13 L24 10&quot;&gt;&lt;/path&gt;&lt;ellipse cx=&quot;7&quot; cy=&quot;25&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;ellipse cx=&quot;20&quot; cy=&quot;23&quot; rx=&quot;4&quot; ry=&quot;5&quot;&gt;&lt;/ellipse&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--card--YSuGN&quot;&gt;&lt;span class=&quot;examples-styles-module--cardIcon--3AxB8&quot;&gt;&lt;svg id=&quot;i-tag&quot; viewBox=&quot;0 0 32 32&quot; width=&quot;18&quot; height=&quot;18&quot; fill=&quot;none&quot; stroke=&quot;currentcolor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;6.25%&quot;&gt;&lt;circle cx=&quot;24&quot; cy=&quot;8&quot; r=&quot;2&quot;&gt;&lt;/circle&gt;&lt;path d=&quot;M2 18 L18 2 30 2 30 14 14 30 Z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardLines--3W_ZB&quot;&gt;&lt;span class=&quot;examples-styles-module--cardTextBar--myLEp&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar--2eJRY&quot;&gt;&lt;/span&gt;&lt;span class=&quot;examples-styles-module--cardSubBar2--3YlPV&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--insetButtonHalf--36nva&quot;&gt;inset&lt;/button&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--maxButtonHalf--1JG2i&quot;&gt;max-inset&lt;/button&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlay--3hWNq&quot;&gt;&lt;div class=&quot;examples-styles-module--overlayBottomInset--3GTOQ&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--overlayBottomMax--5yeCK&quot; style=&quot;background-attachment:fixed;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPgogICAgPGRlZnM+CiAgICAgIDxwYXR0ZXJuIGlkPSJwaW5zdHJpcGUiIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgcGF0dGVyblRyYW5zZm9ybT0icm90YXRlKC00NSkiPgogICAgICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjAiIHkyPSIxMCIgc3Ryb2tlPSJyZ2JhKDI1LDMyLDI1NSwwLjgpIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjcgMyI+PC9saW5lPgogICAgICA8L3BhdHRlcm4+CiAgICA8L2RlZnM+CiAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI3BpbnN0cmlwZSkiPjwvcmVjdD4KICA8L3N2Zz4=)&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--deviceChrome--1KEcg &quot;&gt;&lt;div class=&quot;examples-styles-module--cutout--2j17h examples-styles-module--cutoutIsland--33g48&quot;&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--browserBar--1ScK-&quot; style=&quot;transform:translateY(calc(0 * 100%));transition:transform 0.1s linear&quot;&gt;&lt;div class=&quot;examples-styles-module--browserBarBody--4TDA2&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--actionBar--1e9qs&quot;&gt;&lt;div class=&quot;examples-styles-module--actionHandle--35-bO&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When to use which really depends on your situation. For some UI components it makes sense to move with the live viewport state, like a floating chat button that should always be just above the home indicator.&lt;/p&gt;&lt;p&gt;For other things, like a persistent cookie banner or a full-screen dialog, it can be better to reserve a stable zone that doesn&amp;#x27;t shift when the browser chrome collapses so that users don&amp;#x27;t inadvertently tap the wrong button.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Moves with the live viewport state */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.floating-cta&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Holds a stable reserved zone regardless of browser chrome */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.persistent-zone&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-max-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;browser-support-for-safe-area-max-inset-&quot;&gt;Browser support for &lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset-*&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;As of now, only Chromium implements &lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset-*&lt;/code&gt; so there is no support in (mobile) Safari or Firefox. That means you can&amp;#x27;t depend on it yet and should provide a fallback stack for other browsers:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.bottom-spacer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-max-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + 1rem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how we&amp;#x27;re using the &lt;code class=&quot;language-text&quot;&gt;env()&lt;/code&gt; fallback mechanism to fall back to &lt;code class=&quot;language-text&quot;&gt;safe-area-inset-bottom&lt;/code&gt; if &lt;code class=&quot;language-text&quot;&gt;safe-area-max-inset-bottom&lt;/code&gt; is not supported.&lt;/p&gt;&lt;h2 id=&quot;testing-safe-areas-in-polypane&quot;&gt;Testing safe areas in Polypane&lt;/h2&gt;&lt;p&gt;As covered earlier, safe area bugs are easy to miss during development. Chrome&amp;#x27;s responsive view always reports inset values of 0, and real-device testing tends to get pushed to the end of the project, when fixing layout issues is expensive and bugs have already reached production.&lt;/p&gt;&lt;p&gt;Polypane changes that. You can test safe area insets (and small viewport behavior) directly on your desktop, across multiple devices and orientations simultaneously, as part of your normal development workflow.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Catch real-device layout failures during development, not after shipping.&lt;/strong&gt;&lt;/p&gt;&lt;img src=&quot;static/overlays-8190bd849571d99d1511f3cb9969653c.png&quot; alt=&quot;mobile panes in Polypane showing visualized safe area and small viewport difference&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;what-to-take-away&quot;&gt;What to take away&lt;/h2&gt;&lt;p&gt;Safe areas are not edge cases for unusual devices. They are the reality of modern mobile devices with camera punch holes, notches, dynamic islands, and home indicator bars. Your users are on those devices, and if you want to give them the best experience, you need to make sure your content is not obscured by the system UI.&lt;/p&gt;&lt;p&gt;Make sure your viewport is set to &lt;code class=&quot;language-text&quot;&gt;viewport-fit=cover&lt;/code&gt; so you get the full viewport and that you use &lt;code class=&quot;language-text&quot;&gt;env(safe-area-inset-*)&lt;/code&gt; so all your content is visible and accessible. That gives users the best experience.&lt;/p&gt;&lt;p&gt;Get started with testing safe areas in Polypane today, and catch real-device layout failures during development, not after shipping.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Polypane 29: New network panel, snippet store and Chromium 148]]></title><description><![CDATA[Polypane 29 introduces an updated panel UI, a new network panel for inspecting requests, a new snippet store for discovering and installing…]]></description><link>https://polypane.app/blog/polypane-29-new-network-panel-snippet-store-and-chromium-148/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-29-new-network-panel-snippet-store-and-chromium-148/</guid><pubDate>Mon, 04 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 29 introduces an updated panel UI, a new network panel for inspecting requests, a new snippet store for discovering and installing snippets, and a custom selectors overview in the outline panel, plus many other improvements and fixes.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;re-introducing-the-panel&quot;&gt;Re-introducing the panel&lt;/h2&gt;&lt;p&gt;We redesigned the &lt;a href=&quot;/docs/intro-to-panel/&quot;&gt;panel&lt;/a&gt; navigation to be more familiar to people coming from other browsers. The new single level with overflow menu is a familiar pattern used in devtools in other browsers, while the drag and drop reordering makes it easy to customize the panel to your own needs.&lt;/p&gt;&lt;p&gt;The old multi-level panel navigation was a bit different from what people were used to, and while it had its advantages, it also made it harder for new users to find the features they were looking for.&lt;/p&gt;&lt;video src=&quot;static/paneltabs-fd76a44575aa9d2504c95c94b0c3ef76.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;With the new design, we hope to make it easier for people to get started with Polypane and also for them to find the many features that Polypane has to offer.&lt;/p&gt;&lt;p&gt;It also made it really easy for us to add a new panel:&lt;/p&gt;&lt;h2 id=&quot;network-panel&quot;&gt;Network panel&lt;/h2&gt;&lt;p&gt;The new &lt;a href=&quot;/docs/network-panel/&quot;&gt;network panel&lt;/a&gt; is our take on the network panel. Built from the ground up with Polypane&amp;#x27;s multi-pane architecture in mind and with a focus on showing you actionable data, the new network panel starts off with a high-level overview of your page&amp;#x27;s load performance and then lets you drill down.&lt;/p&gt;&lt;video src=&quot;static/network-b941333109e976b3882ba7117a091ea8.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;The top of the network panel is meant for glancing: &lt;code class=&quot;language-text&quot;&gt;domContentLoaded&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;load&lt;/code&gt; timing, Web Vitals timings and the number of requests and origins. All of these are shown as ranges when they differ per pane.&lt;/p&gt;&lt;img src=&quot;static/network-overview-65a3162bd006a17638d88990d3fbdadf.png&quot; class=&quot;imgshadow&quot; alt=&quot;Network summary&quot;/&gt;&lt;p&gt;Polypane then shows you the origins your page connects to. Each origin has its own performance characteristics, so it&amp;#x27;s really useful to see this split out.&lt;/p&gt;&lt;p&gt;It can be &lt;em&gt;quite the eye-opener&lt;/em&gt; seeing all the origins your page connects to.&lt;/p&gt;&lt;img src=&quot;static/network-stats-264c9cbbd07ad2b14889bb5d1085e78b.png&quot; class=&quot;imgshadow&quot; alt=&quot;origins with stats&quot;/&gt;&lt;p&gt;Each origin shows a quick overview of the type of connection and number of requests. If you&amp;#x27;re accidentally connecting to a slow HTTP/1 server, this tells you instantly.&lt;/p&gt;&lt;p&gt;Your main origin is always expanded, showing you how many requests are active, stalled or completed, how many concurrent connections the origin supports and the average download time.&lt;/p&gt;&lt;p&gt;The request table is fully featured and interactive, so you can really dig into the performance characteristics of your page and find bottlenecks and issues.&lt;/p&gt;&lt;img src=&quot;static/network-requests-8c3bccdab9d6789b2a42047707a140c1.png&quot; class=&quot;imgshadow&quot; alt=&quot;origins with request table&quot;/&gt;&lt;p&gt;By default the table shows the load waterfall so you know which resources depend on which other resources and how long they took to load, and you can sort the table by any column to find the slowest resources, or the ones that are blocking the most other resources.&lt;/p&gt;&lt;p&gt;For each request you can show the headers split out in general info, response and request headers. If headers are different per pane, they&amp;#x27;re annotated and the differences are highlighted so you can easily spot them.&lt;/p&gt;&lt;img src=&quot;static/network-headers-2183677eceb7827116bb7147e2f56b24.png&quot; class=&quot;imgshadow&quot; alt=&quot;Request headers&quot;/&gt;&lt;p&gt;The network panel lets you see things at a glance or really dive deep into the performance characteristics of your page, and we have a lot of plans to expand on this in the future.&lt;/p&gt;&lt;p&gt;If you have suggestions for what you&amp;#x27;d like to see in the network panel, let us know!&lt;/p&gt;&lt;h2 id=&quot;server-bottleneck-monitoring&quot;&gt;Server bottleneck monitoring&lt;/h2&gt;&lt;p&gt;Most dev servers use HTTP/1, which only supports up to 6 concurrent connections. That can lead to requests in Polypane being stalled while it&amp;#x27;s waiting for a connection to become available.&lt;/p&gt;&lt;p&gt;Some people mistakenly think that this is a Polypane issue. In reality, we&amp;#x27;re just waiting for the server to send data. Starting in 29, we show a warning in the address bar and the network panel when we notice this server bottleneck.&lt;/p&gt;&lt;img src=&quot;static/loadperfwarning-2a5f929f789b2f62ede2c88c593a1779.png&quot; class=&quot;imgshadow&quot; alt=&quot;Load performance warning in the address bar&quot;/&gt;&lt;p&gt;We&amp;#x27;re also expanding our connection whitelist, that allows us to open more connections to servers running on localhost, to Docker IPs running on the local subnet. This means faster local servers even if they are running in a container.&lt;/p&gt;&lt;h2 id=&quot;snippet-store&quot;&gt;Snippet store&lt;/h2&gt;&lt;p&gt;The new &lt;a href=&quot;/snippets/&quot;&gt;Snippets store&lt;/a&gt; is the place to find and install snippets and snippet collections created by the Polypane team and our community. You can find snippets for all kinds of use cases like accessibility, performance, quick debugging and even some that are just for fun.&lt;/p&gt;&lt;h3 id=&quot;installing-snippets&quot;&gt;Installing snippets&lt;/h3&gt;&lt;p&gt;Open the snippet store in Polypane to browse the available snippets and install the ones you find useful right from the website. Click an install button and Polypane will automatically install the snippet for you, no need to download files or import them manually.&lt;/p&gt;&lt;p&gt;After installing, the snippet is available from the &lt;a href=&quot;/docs/snippets/&quot;&gt;snippets panel&lt;/a&gt; and the &lt;a href=&quot;/docs/command-bar/&quot;&gt;command bar&lt;/a&gt; and you can apply it to all panes or specific panes as needed.&lt;/p&gt;&lt;video src=&quot;static/installsnippet-7b8f7fa3d3936aab2ee6e64460c7a828.mp4&quot; controls=&quot;&quot; style=&quot;margin:2rem auto;max-width:100%;display:block&quot;&gt;&lt;/video&gt;&lt;p&gt;In other browsers, snippets can be downloaded as a file so you can import them into Polypane.&lt;/p&gt;&lt;h3 id=&quot;contributing-snippets&quot;&gt;Contributing snippets&lt;/h3&gt;&lt;p&gt;You can contribute your own snippets to the store by submitting a PR to the &lt;a href=&quot;https://github.com/Polypane/snippets&quot;&gt;Polypane snippets repository&lt;/a&gt; on GitHub. You can find all the instructions on how to do that in the README in that repository.&lt;/p&gt;&lt;p&gt;We are looking forward to seeing what snippets the community will come up with!&lt;/p&gt;&lt;h2 id=&quot;snippet-live-preview&quot;&gt;Snippet live preview&lt;/h2&gt;&lt;p&gt;Snippets replaced our old Live CSS Preview panel with a much more powerful and flexible system, but in the process we lost the ability to see your CSS be directly applied to the page as you type it. The new live preview option brings it back!&lt;/p&gt;&lt;img src=&quot;static/livesnippet-6db5950047b89ae44f3e34de7fdff0b0.png&quot; class=&quot;imgshadow&quot; alt=&quot;Live preview option for snippets&quot;/&gt;&lt;p&gt;When editing or adding a CSS snippet you can now toggle the &amp;quot;live preview&amp;quot; on to see the changes you make to your CSS snippet directly applied to the page.&lt;/p&gt;&lt;p&gt;Even with the live preview, we will still automatically load any Google fonts you use in your snippets, so you can use those without having to worry about it.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Kari&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;h2 id=&quot;polypane-peek-improvements&quot;&gt;Polypane Peek improvements&lt;/h2&gt;&lt;p&gt;We made two quite significant improvements to &lt;a href=&quot;/docs/polypane-peek/&quot;&gt;Polypane peek&lt;/a&gt;: iframes are now inspectable, and grid and flex visualisations.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Peek lets you hold &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌥&lt;/kbd&gt; while hovering over an element to quickly see the styles, dimensions, accessibility information and more. It&amp;#x27;s a great way to inspect elements without having to open the Elements panel and find the element in the tree view.&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;grid-and-flex-visualisations&quot;&gt;Grid and flex visualisations&lt;/h3&gt;&lt;p&gt;We now automatically show visualisations for grid and flex containers when inspecting them with Peek. While you could already turn these on from the tree view in the Elements panel, having them be available in Peek makes it much easier to quickly check if things are laid out the way you expect.&lt;/p&gt;&lt;img src=&quot;static/flexviz-42f63cb0a733b88b18d287ccb1929ffa.png&quot; class=&quot;imgshadow&quot; alt=&quot;Flex visualisation in Polypane Peek&quot;/&gt;&lt;p&gt;Now, it wouldn&amp;#x27;t be a Polypane feature if we didn&amp;#x27;t do a little bit extra in our visualisations compared to what you can find in other browsers.&lt;/p&gt;&lt;p&gt;Polypane highlights the child nodes instead of the areas. This means that you don&amp;#x27;t just see the container configuration, but you can also see the alignment of child nodes within those areas.&lt;/p&gt;&lt;h3 id=&quot;inspectable-iframes&quot;&gt;Inspectable iframes&lt;/h3&gt;&lt;p&gt;When you hover over an iframe, we now correctly show the information and element highlight, something that was previously not possible.&lt;/p&gt;&lt;img src=&quot;static/iframe-inspect-86b078d10768a799ed23048378d7232c.png&quot; class=&quot;imgshadow&quot; alt=&quot;Inspecting an iframe with Peek&quot;/&gt;&lt;h2 id=&quot;outline-panel&quot;&gt;Outline panel&lt;/h2&gt;&lt;p&gt;We added a new overview, and improved the accessibility tree in the &lt;a href=&quot;/docs/outline-panel/&quot;&gt;outline panel&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;custom-selectors-overview&quot;&gt;Custom selectors overview&lt;/h3&gt;&lt;p&gt;Between headers, landmarks, images and links, the &lt;a href=&quot;/docs/outline-panel/&quot;&gt;outline panel&lt;/a&gt; already gives you a great overview of the structure of your page. But sometimes you need an overview of something more specific.&lt;/p&gt;&lt;p&gt;With the new custom selectors overview, you can now use any CSS selector to get an overview of all the elements that match that selector on your page. For example, use &lt;code class=&quot;language-text&quot;&gt;table&lt;/code&gt; to find all tables, &lt;code class=&quot;language-text&quot;&gt;[aria-live]&lt;/code&gt; to get an overview of all the live regions on your page, or use &lt;code class=&quot;language-text&quot;&gt;button, [role=&amp;quot;button&amp;quot;]&lt;/code&gt; to get an overview of all the buttons split out between real ones and those that have a button role. This is especially useful for accessibility debugging, but can also be used for finding specific elements on the page.&lt;/p&gt;&lt;img src=&quot;static/customselectors2-b9af106129b700486911dfdf7a1f5ab4.png&quot; alt=&quot;Custom selectors overview in the outline panel&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;When you add multiple selectors, separated by a comma, the outline panel automatically groups them and gives each selector a unique color so you can easily see which elements match what selector in the overlay.&lt;/p&gt;&lt;h3 id=&quot;accessibility-tree-improvements&quot;&gt;Accessibility tree improvements&lt;/h3&gt;&lt;p&gt;The accessibility tree now lists the description for an element and makes it easy to inspect the referenced element IDs.&lt;/p&gt;&lt;img src=&quot;static/acctreedesc-0793a6e52cdef7caaebce2358a9ded3a.png&quot; alt=&quot;Accessibility tree showing element descriptions&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Additionally, the tree should now both update more quickly and also be more stable when you&amp;#x27;re working with a page that has a lot of accessibility tree changes so you don&amp;#x27;t lose your place in the tree when you&amp;#x27;re inspecting accessibility issues.&lt;/p&gt;&lt;h2 id=&quot;elements-panel-improvements&quot;&gt;Elements panel improvements&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/elements-panel/&quot;&gt;elements panel&lt;/a&gt; got a few nice bug fixes and improvements in this release, making for a more stable environment even when dealing with a DOM with lots of events, changes and hot reloading logic.&lt;/p&gt;&lt;h3 id=&quot;much-easier-pseudo-state-toggling&quot;&gt;Much easier pseudo state toggling&lt;/h3&gt;&lt;p&gt;Toggling pseudo states like &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;:active&lt;/code&gt; is now much easier to do. Instead of opening the pseudo state menu in the navigator, the style tab now has toggle buttons for all the pseudo styles you have defined for an element. Co-locating these with the styles for that pseudo state makes it so much easier to use, and a big thanks to Eric for coming up with this feature.&lt;/p&gt;&lt;img src=&quot;static/statetoggles-2a237d8dbee513e49230a1a5a8e86f77.png&quot; alt=&quot;State toggles in the style panel&quot; class=&quot;imgshadow&quot;/&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Eric&lt;/strong&gt; for the excellent feature suggestion!&lt;/aside&gt;&lt;p&gt;Oh, by the way, it also works for &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; rules!&lt;/p&gt;&lt;img src=&quot;static/startingstyletoggle-0002efc1c40a4613f40dd09997dc16e9.png&quot; alt=&quot;Starting style toggle in the style panel&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;more-info-in-the-element-a11y-tab&quot;&gt;More info in the Element A11y tab&lt;/h3&gt;&lt;p&gt;For the A11y tab we made two improvements that make it easier to see what&amp;#x27;s going on. Firstly, it now shows the &lt;code class=&quot;language-text&quot;&gt;aria-describedby&lt;/code&gt; value if set, so you know what additional information is being provided to assistive technologies.&lt;/p&gt;&lt;img src=&quot;static/a11ytab-59f9259a97ae724d26db8167f4d9f935.png&quot; alt=&quot;Accessibility tab listing more info&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Secondly, we now link all &lt;code class=&quot;language-text&quot;&gt;labelledby&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;describedby&lt;/code&gt; elements right below their value and make it easy to see how your accessible name and description are being constructed.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Eric&lt;/strong&gt; for your accessibility insights!&lt;/aside&gt;&lt;h3 id=&quot;tree-view-keyboard-interactions&quot;&gt;Tree view keyboard interactions&lt;/h3&gt;&lt;p&gt;The tree view already has arrow keys navigation and lets you hide elements using &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;h&lt;/kbd&gt;. Now you can also use the &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;del&lt;/kbd&gt; key to remove elements from the tree view.&lt;/p&gt;&lt;p&gt;Deleting elements was already available in the context menu, but if you&amp;#x27;re already on the keyboard, it&amp;#x27;s much faster to just hit &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;del&lt;/kbd&gt;.&lt;/p&gt;&lt;h3 id=&quot;quicker-data-refreshing&quot;&gt;Quicker data refreshing&lt;/h3&gt;&lt;p&gt;We&amp;#x27;ve updated the logic that determines when we need to update element styles. This makes it work much better for modern dev servers with very precise style updates and injection.&lt;/p&gt;&lt;h2 id=&quot;passkeys-keychain-integration-on-macos&quot;&gt;Passkeys keychain integration on macOS&lt;/h2&gt;&lt;p&gt;It took us a while to go through Apple&amp;#x27;s process to integrate with the passkey system, but Polypane now has first-class support for passkeys on macOS. As a result, the Apple password manager browser extension should now also work much better in Polypane.&lt;/p&gt;&lt;img src=&quot;static/passkeys-0583e5b7d982ec495fc332e302bc0e08.png&quot; alt=&quot;Passkeys keychain integration on macOS&quot;/&gt;&lt;p&gt;Passkey support on Windows was already supported through Windows Security, and on Linux, which has no centralised security system, passkeys are supported by installing an extension that supports them, like 1Password.&lt;/p&gt;&lt;h2 id=&quot;whatsapp-preview-in-meta-panel&quot;&gt;WhatsApp preview in Meta panel&lt;/h2&gt;&lt;p&gt;As our 11th(!) &lt;a href=&quot;/social-media-previews/&quot;&gt;social media visualisation&lt;/a&gt; we added support for WhatsApp&amp;#x27;s link previews:&lt;/p&gt;&lt;img src=&quot;static/whatsapp-light-2db6e2f494a9e55f9dcab7d65602e5ec.png&quot; alt=&quot;WhatsApp preview in light mode&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;p&gt;It&amp;#x27;s pixel perfect in both Light and Dark mode (but you expected that, right?)&lt;/p&gt;&lt;img src=&quot;static/whatsapp-dark-b91998b29feaa3ca5d96a1d15019a41b.png&quot; alt=&quot;WhatsApp preview in dark mode&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;h2 id=&quot;devtools-panel-inspect-shared-workers-and-service-workers&quot;&gt;Devtools panel: Inspect shared workers and service workers&lt;/h2&gt;&lt;p&gt;If you&amp;#x27;ve ever needed to inspect shared workers or service workers, you know that it&amp;#x27;s a bit of a hassle to have to go to &lt;code class=&quot;language-text&quot;&gt;chrome://inspect&lt;/code&gt; and find workers there. In Polypane we now just show shared workers and service workers in the devtools panel dropdown when you have a page open that has them.&lt;/p&gt;&lt;img src=&quot;static/workerselection-89fac6b972902c4bb81ea596ae74e128.png&quot; class=&quot;imgshadow&quot; alt=&quot;Worker selection in devtools&quot;/&gt;&lt;p&gt;Just select the worker you want to inspect from the dropdown and you&amp;#x27;ll get a full devtools window to inspect the worker, just like you would in Chrome.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Przemyslaw&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;h2 id=&quot;smaller-improvements-and-fixes&quot;&gt;Smaller improvements and fixes&lt;/h2&gt;&lt;h3 id=&quot;quickly-inspect-elements-in-the-elements-panel-from-the-find-elements-bar&quot;&gt;Quickly inspect elements in the Elements panel from the Find elements bar&lt;/h3&gt;&lt;p&gt;When using the &lt;a href=&quot;/docs/find-elements/&quot;&gt;find element feature&lt;/a&gt; to find elements on the page, you can now jump to the Elements panel with that element selected by hitting &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ enter&lt;/kbd&gt;. This makes it much easier to inspect the element you found in the Elements panel without having to find it again in the tree view.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; alt=&quot;The find HTML in page popup&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Ricky&lt;/strong&gt; for this suggestion!!&lt;/aside&gt;&lt;h3 id=&quot;find-elements-inside-shadow-dom&quot;&gt;Find elements inside shadow DOM&lt;/h3&gt;&lt;p&gt;We have a new helper function &lt;code class=&quot;language-text&quot;&gt;$$$()&lt;/code&gt; in the &lt;a href=&quot;/docs/console/&quot;&gt;Console&lt;/a&gt; that works just like &lt;code class=&quot;language-text&quot;&gt;$$()&lt;/code&gt; but also searches inside shadow DOM. If you make use of web components, this will help you find elements that are otherwise hidden from the console&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;$$()&lt;/code&gt; function!&lt;/p&gt;&lt;img src=&quot;static/shadowdomsearch-a7cc6dff8c4401bd9519073fa59bcd8c.png&quot; alt=&quot;console showing $$(form) returning two elements but $$$(form) returning three.&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;h3 id=&quot;form-autofilling&quot;&gt;Form autofilling&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;/docs/form-autofill/&quot;&gt;Form autofilling&lt;/a&gt; is now a little bit smarter. If you right-click an input field, we now automatically move matching lists to the top of the custom values context menu. Right click a field with a type of email, and the first item in the list of custom values is email addresses. If the autocomplete asks for a postcode, then the first item in the list of custom values is Postcodes. This way, you can fill in common fields with the right values without having to scroll through your entire list of custom values.&lt;/p&gt;&lt;img src=&quot;static/custom-f9357ab604e2ec201fc82ccd1443862c.png&quot; alt=&quot;Custom values with payment info on top, and email address on top for email fields&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;p&gt;Speaking of email addresses, there&amp;#x27;s now also the option to fill the input with a custom email address. By default we use your account email address, but you can change that right from the context menu to whatever is most useful to you. This makes testing real forms even easier, since you can quickly fill in the form with a valid email address that you have access to.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Tom&lt;/strong&gt; for this great idea!&lt;/aside&gt;&lt;h3 id=&quot;json-viewer&quot;&gt;JSON viewer&lt;/h3&gt;&lt;p&gt;When opening a JSON file, the built-in &lt;a href=&quot;/docs/json-and-xml-viewer/&quot;&gt;JSON viewer&lt;/a&gt; now also makes the JSON available in the console as &lt;code class=&quot;language-text&quot;&gt;window.json&lt;/code&gt;. This means that you can easily access the data from the console to do further debugging or testing with it, or when you want to drill down and get a programmatic overview of the path rather than just inspect it.&lt;/p&gt;&lt;h2 id=&quot;chromium-148&quot;&gt;Chromium 148&lt;/h2&gt;&lt;p&gt;Polypane 29 is built on Chromium 148.0.7778.56. Notably available in this release is the new html-in-canvas API that we have enabled by default. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-29&quot;&gt;Get Polypane 29&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14-day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-29-changelog&quot;&gt;Polypane 29 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Network panel&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Snippet store&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Panel: Switch to single-line with overflow tab bar&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Grid/Flex visualisations in Polypane Peek&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Outline panel: Custom selector overview&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Passkeys keychain integration on macOS&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Meta panel: WhatsApp preview&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Devtools panel: Inspect shared workers and service workers (Thanks Przemyslaw!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Snippets panel: live preview option (Thanks Kari!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Activated html-in-canvas API by default&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Chromium 148.0.7778.56&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Polypane Peek: iframes are now inspectable and can be selected&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Load performance for custom local domains (Thanks Mike!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Confirmation dialogs are now deduplicated when called multiple times in a row (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; JSON viewer: Makes data available in the console as window.json&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: A11y tab now links all labelledby elements (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: A11y tab now shows aria-describedby value (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Stable references to elements for hot reloading dev servers (Thanks Matyáš!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Load new styles on injection by hot reload servers (Thanks Matyáš!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Toggle pseudo styles in style panel (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: del key now removes element in tree view&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: new $$$() helper that also searches shadow DOM.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Accessibility tree now shows a description as well as the referenced IDs (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Basic auth popup now follows the system design&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Form autofill: Ability to use a default email address (Thanks Tom!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Form autofill: Move lists matching the element to the top&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Find elements: cmd/ctrl + enter now shows element in Elements panel (Thanks Ricky!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug tools: Improve wording in Color contrast tool (Thanks Ana!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippets Editor: Improved layout when editing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Traffic lights alignment on macOS 26 is now improved&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Session management: Add confirmation dialog before deleting session&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Pane resizing performance when you have a lot of panes open&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; More deterministic page loading after click actions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Integrations: new Marker.io logo&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Improve accessible name calculation labeled by elements with roles without accname (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Events badge was not visible in tree view (Thanks Ashlee!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Allow editing the color of an image-only background property&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Copy CSS for keyframes was missing the animation name&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Font weight wasn&amp;#x27;t accurate for contrast calculation in A11y tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: Previous tab shortcut was not working (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: New window from panel did not always create new tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: New tab should focus access bar (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: Add extension button did not work&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Full page screenshots: use correct viewport height when making screenshots&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Context menu: &amp;#x27;Save link as...&amp;#x27; option did not work&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Prevent accessibility tree from collapsing focused elements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Prevent double tooltips when hovering over elements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Styles: Prevent React router from trashing styles on load (Thanks Nathan!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Console panel: Prevent extra white space in styled console messages&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; &amp;#x27;No internet connection&amp;#x27; warning is now aligned correctly&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: The suggested file name now includes correct name&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[The Intl API: The best browser API you're not using]]></title><description><![CDATA[Chances are you've used  Moment.js ,  date-fns ,  Luxon , or  numeral.js  at some point. Developers have relied on these libraries for years…]]></description><link>https://polypane.app/blog/the-intl-api-the-best-browser-api-youre-not-using/</link><guid isPermaLink="false">https://polypane.app/blog/the-intl-api-the-best-browser-api-youre-not-using/</guid><pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Chances are you&amp;#x27;ve used &lt;strong&gt;Moment.js&lt;/strong&gt;, &lt;strong&gt;date-fns&lt;/strong&gt;, &lt;strong&gt;Luxon&lt;/strong&gt;, or &lt;strong&gt;numeral.js&lt;/strong&gt; at some point. Developers have relied on these libraries for years to format dates, numbers, and currencies. Those are all very useful libraries, but they also come with a cost: they add kilobytes to your download size and they require client-side code parsing:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Library&lt;/th&gt;&lt;th&gt;Size&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Moment.js&lt;/td&gt;&lt;td&gt;295 kB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;date-fns&lt;/td&gt;&lt;td&gt;77 kB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;luxon.js&lt;/td&gt;&lt;td&gt;82 kB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;numeral.js&lt;/td&gt;&lt;td&gt;11 kB&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; API is baseline widely available (except for &lt;code class=&quot;language-text&quot;&gt;Intl.DurationFormat&lt;/code&gt;. It works in all current evergreen browsers, but hasn&amp;#x27;t been around long enough to qualify for &amp;quot;widely available&amp;quot;.) and can handle almost all of your formatting requirements directly in the browser, with zero kB of download and no JS bundle parsing required. It also knows your users&amp;#x27; locale preferences so you can format dates and numbers in a way that feels natural to them without any extra work.&lt;/p&gt;&lt;h2 id=&quot;prefer-video&quot;&gt;Prefer video?&lt;/h2&gt;&lt;p&gt;This article is a written version of a talk by Kilian Valkhof, Polypane&amp;#x27;s creator. You can watch the video here:&lt;/p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom:56.25%;position:relative;height:0;overflow:hidden&quot;&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/mhzJ1UFlRjw&quot; title=&quot;The Intl API: The Best Browser API You Are Not Using&quot; frameBorder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot;position:absolute;top:0;left:0;width:100%;height:100%&quot;&gt;&lt;/iframe&gt; &lt;/div&gt;&lt;p&gt;&lt;em&gt;Interested in having this talk at your conference or meetup? &lt;a href=&quot;https://kilianvalkhof.com/speaking/&quot;&gt;Get in touch!&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;why-you-need-an-internationalization-api&quot;&gt;Why you need an internationalization API&lt;/h2&gt;&lt;p&gt;You might be thinking: &amp;quot;My site is only in one language. Why would I need an internationalization API?&amp;quot;&lt;/p&gt;&lt;p&gt;That&amp;#x27;s the most common reason developers skip over &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt;. But &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; is less about translating your UI and more about &lt;strong&gt;formatting data correctly for your users&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Consider a date like &lt;code class=&quot;language-text&quot;&gt;09/12/2025&lt;/code&gt;. If you&amp;#x27;re from the US, you&amp;#x27;d read it as September 12th. Almost anywhere else in the world, you&amp;#x27;d read it as December 9th. That&amp;#x27;s quite a big difference, and if a carelessly (un)formatted date leads to someone missing an appointment or even just confusion, that reflects very poorly on your site.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; API formats dates, numbers, currencies, lists, and text in a way that fits the user&amp;#x27;s locale. Even if your site is just in English, users from different English speaking countries still have different formatting conventions. &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; handles all of that for you, so you don&amp;#x27;t have to worry about it.&lt;/p&gt;&lt;h3 id=&quot;what-intl-gives-you&quot;&gt;What Intl gives you&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;0 kB added to your bundle&lt;/strong&gt;: it&amp;#x27;s built into the browser&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Baseline widely available&lt;/strong&gt;: works in every major browser&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Locale-aware&lt;/strong&gt;: knows not just languages but country-specific conventions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;: native implementations are significantly faster than userland libraries&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The APIs we&amp;#x27;ll cover:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.DateTimeFormat&lt;/code&gt;: format dates and times&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.RelativeTimeFormat&lt;/code&gt;: &amp;quot;yesterday&amp;quot;, &amp;quot;in 3 days&amp;quot;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.DurationFormat&lt;/code&gt;: &amp;quot;2 hours, 5 minutes and 30 seconds&amp;quot;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.NumberFormat&lt;/code&gt;: format numbers, currencies, and units&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.ListFormat&lt;/code&gt;: &amp;quot;Apples, Oranges, and Bananas&amp;quot;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.PluralRules&lt;/code&gt;: figure out plural forms correctly&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Segmenter&lt;/code&gt;: break text into words, sentences, or graphemes&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Collator&lt;/code&gt;: sort strings in a locale-aware way&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;what-intl-doesnt-do&quot;&gt;What Intl doesn&amp;#x27;t do&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ll get into plenty of examples later on in this article but a common misunderstanding, or maybe wish, is that &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; also handles data wrangling. Unfortunately, &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; is a formatting API, not a calculation or measurement API. It just takes objects (like dates, numbers and arrays) and turns them into strings.&lt;/p&gt;&lt;p&gt;For example if you want to format a relative time compared to &lt;em&gt;now&lt;/em&gt;, like to show when a blog post was released compared to the current time, you need to calculate the difference yourself and then pass that to &lt;code class=&quot;language-text&quot;&gt;Intl.RelativeTimeFormat&lt;/code&gt;. We have an example of how to do that in the section on &lt;code class=&quot;language-text&quot;&gt;RelativeTimeFormat&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Later on in the article you&amp;#x27;ll also see that it can format numbers with units (like degrees Celsius or miles) but it has no conception of what those units mean. So while it knows to put a &lt;code class=&quot;language-text&quot;&gt;°C&lt;/code&gt; after a number when you ask for Celsius, it doesn&amp;#x27;t know how to convert from Fahrenheit to Celsius. It&amp;#x27;s just takes a number and returns a string with an unit in it.&lt;/p&gt;&lt;h2 id=&quot;how-the-intl-api-works&quot;&gt;How the Intl API works&lt;/h2&gt;&lt;p&gt;All &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; functions have roughly the same shape:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Pick a locale.&lt;/li&gt;&lt;li&gt;Pick some options.&lt;/li&gt;&lt;li&gt;Create a formatter.&lt;/li&gt;&lt;li&gt;Reuse that formatter with your data.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here&amp;#x27;s the general setup with &lt;code class=&quot;language-text&quot;&gt;Intl.DateTimeFormat&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; locale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;dateStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;full&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;timeStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, once you have the formatter, you pass it the value you want to format:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; publishedAt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;2026-04-07T16:45:00Z&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publishedAt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Tuesday, April 7, 2026 at 4:45 PM&amp;quot;&lt;/span&gt;

formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;2026-06-01T09:00:00Z&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Monday, June 1, 2026 at 9:00 AM&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That same pattern shows up throughout &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PluralRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The constructor arguments are consistent too:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A &lt;strong&gt;locale string&lt;/strong&gt; such as &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;fr-FR&amp;#x27;&lt;/code&gt;. If you omit it, the browser uses the user&amp;#x27;s system locale. You can also pass &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; to use the browser&amp;#x27;s default locale explicitly, which you need if you also want to set options.&lt;/li&gt;&lt;li&gt;An &lt;strong&gt;options object&lt;/strong&gt; that controls the output. The available options differ per formatter, but the setup pattern stays the same.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Creating the formatter is the expensive part, because that loads the locale data and sets up the internal structures. Calling &lt;code class=&quot;language-text&quot;&gt;.format()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.select()&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;.compare()&lt;/code&gt; afterwards is cheap. That means you should create the formatter once and reuse it when you&amp;#x27;re handling a lot of values.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Good: create once, reuse many times.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;dateStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

dates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Less good: create a new formatter every time.&lt;/span&gt;
dates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;dateStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That matters most with &lt;code class=&quot;language-text&quot;&gt;Intl.Collator&lt;/code&gt;, the API that lets you sort strings in a locale-aware way. Sorting can call the comparison function many times, so you definitely want to create the collator once and reuse it.&lt;/p&gt;&lt;h3 id=&quot;setting-the-locale&quot;&gt;Setting the locale&lt;/h3&gt;&lt;p&gt;For locales, you can use any valid BCP 47 language tag, such as &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;fr-FR&amp;#x27;&lt;/code&gt;. You can also specify a list of locales, and the browser will pick the first one it supports.&lt;/p&gt;&lt;p&gt;Support for locales differs between browser and operating system combinations. The browser will know &lt;em&gt;of&lt;/em&gt; thousands of locales, but it will actually only have data (like translated units or date formats) for a few hundred of those. By passing a list of locales, you can provide fallbacks in case the browser doesn&amp;#x27;t have data for the first one. For example, if you specify &lt;code class=&quot;language-text&quot;&gt;[&amp;#x27;en-US&amp;#x27;, &amp;#x27;en-GB&amp;#x27;]&lt;/code&gt;, the browser will use US English if it has that, but if it doesn&amp;#x27;t, it will fall back to British English.&lt;/p&gt;&lt;p&gt;If you don&amp;#x27;t specify a locale, the formatter uses the browser&amp;#x27;s locale. You can set it to &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; to get the same effect. That means you can create a formatter that automatically adapts to the user&amp;#x27;s locale without any extra work. Alternatively you can explicitly ask for &lt;code class=&quot;language-text&quot;&gt;navigator.language&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;navigator.languages&lt;/code&gt; to get the user&amp;#x27;s language preferences and pass those to the formatter:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Choose a specific locale&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Pass a list of locales, the browser picks the first one it supports&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-GB&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Use the browser&amp;#x27;s locale&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Use the browser&amp;#x27;s locale but specify options too&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;language&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Use the user&amp;#x27;s full language preference list&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (the browser picks the first one it supports)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;languages&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;navigator.languages&lt;/code&gt; is an array like &lt;code class=&quot;language-text&quot;&gt;[&amp;#x27;en-US&amp;#x27;, &amp;#x27;en&amp;#x27;, &amp;#x27;nl&amp;#x27;]&lt;/code&gt;, so works like a list of locales you can pass directly to the formatter.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;What&amp;#x27;s the difference between a language and a locale?&lt;/strong&gt;
A locale is a language as spoken in a specific country. For example, both the US and the UK speak English, but they have different formatting conventions for dates and numbers. So you have &lt;code class=&quot;language-text&quot;&gt;en-US&lt;/code&gt; for American English and &lt;code class=&quot;language-text&quot;&gt;en-GB&lt;/code&gt; for British English. If you just specify &lt;code class=&quot;language-text&quot;&gt;en&lt;/code&gt;, the browser will pick a default locale for that language, which is usually the most common one.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;dates--times&quot;&gt;Dates &amp;amp; Times&lt;/h2&gt;&lt;p&gt;Let&amp;#x27;s start with the APIs that have the clearest naming: the ones for formatting dates and times.&lt;/p&gt;&lt;h3 id=&quot;intldatetimeformat&quot;&gt;Intl.DateTimeFormat&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.DateTimeFormat&lt;/code&gt; is the API for formatting dates and times.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;dateStyle&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;(none)&lt;/option&gt;&lt;option value=&quot;full&quot; selected=&quot;&quot;&gt;full&lt;/option&gt;&lt;option value=&quot;long&quot;&gt;long&lt;/option&gt;&lt;option value=&quot;medium&quot;&gt;medium&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;timeStyle&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;(none)&lt;/option&gt;&lt;option value=&quot;full&quot;&gt;full&lt;/option&gt;&lt;option value=&quot;long&quot;&gt;long&lt;/option&gt;&lt;option value=&quot;medium&quot;&gt;medium&lt;/option&gt;&lt;option value=&quot;short&quot; selected=&quot;&quot;&gt;short&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;hour12&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;default&quot; selected=&quot;&quot;&gt;locale default&lt;/option&gt;&lt;option value=&quot;true&quot;&gt;true&lt;/option&gt;&lt;option value=&quot;false&quot;&gt;false&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;timeZone&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;UTC&quot; selected=&quot;&quot;&gt;UTC&lt;/option&gt;&lt;option value=&quot;Europe/Amsterdam&quot;&gt;Europe/Amsterdam&lt;/option&gt;&lt;option value=&quot;America/New_York&quot;&gt;America/New_York&lt;/option&gt;&lt;option value=&quot;Asia/Tokyo&quot;&gt;Asia/Tokyo&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.DateTimeFormat(undefined, {
  dateStyle: &amp;quot;full&amp;quot;,
  timeStyle: &amp;quot;short&amp;quot;,
  timeZone: &amp;quot;UTC&amp;quot;
});
formatter.format(new Date(&amp;quot;2026-04-07T16:45:00.000Z&amp;quot;));&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Tuesday, April 7, 2026 at 4:45 PM&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Tuesday, April 7, 2026 at 4:45 PM&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;dinsdag 7 april 2026 om 16:45&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;mardi 7 avril 2026 à 16:45&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2026年4月7日火曜日 16:45&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;الثلاثاء، ٧ أبريل ٢٠٢٦ في ٤:٤٥ م&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;At its simplest:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;4/7/2026&amp;quot; (in en-US)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The default output is a bit terse. Most of the time you&amp;#x27;ll want to specify a style. The easiest way is with &lt;code class=&quot;language-text&quot;&gt;dateStyle&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;timeStyle&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;fr-FR&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;dateStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;full&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;full&amp;#x27; | &amp;#x27;long&amp;#x27; | &amp;#x27;medium&amp;#x27; | &amp;#x27;short&amp;#x27;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;timeStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;full&amp;#x27; | &amp;#x27;long&amp;#x27; | &amp;#x27;medium&amp;#x27; | &amp;#x27;short&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;mercredi 8 avril 2026 à 09:19&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;#x27;s nice here is that you didn&amp;#x27;t have to know that &amp;quot;mercredi&amp;quot; is Wednesday in French, or that &amp;quot;avril&amp;quot; is April, or have arrays of days and months somewhere. You ask the browser for French, and the browser already knows.&lt;/p&gt;&lt;p&gt;When you need more control, you can specify exactly which parts of the date to show and how to format them:&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;weekday&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;long&quot; selected=&quot;&quot;&gt;long&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;day&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot; selected=&quot;&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot;&gt;2-digit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;hour&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot; selected=&quot;&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot;&gt;2-digit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;minute&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot; selected=&quot;&quot;&gt;2-digit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;second&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot; selected=&quot;&quot;&gt;2-digit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;month&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot;&gt;2-digit&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;long&quot; selected=&quot;&quot;&gt;long&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;year&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;numeric&quot; selected=&quot;&quot;&gt;numeric&lt;/option&gt;&lt;option value=&quot;2-digit&quot;&gt;2-digit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;era&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;none&quot; selected=&quot;&quot;&gt;none&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;long&quot;&gt;long&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.DateTimeFormat(undefined, {
  weekday: &amp;quot;long&amp;quot;,
  day: &amp;quot;numeric&amp;quot;,
  hour: &amp;quot;numeric&amp;quot;,
  minute: &amp;quot;2-digit&amp;quot;,
  second: &amp;quot;2-digit&amp;quot;,
  month: &amp;quot;long&amp;quot;,
  year: &amp;quot;numeric&amp;quot;
});
formatter.format(new Date());&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Monday, September 14, 2026 at 5:30:41 PM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If you specify individual parts like this, only those parts will appear in the output. Mixing &lt;code class=&quot;language-text&quot;&gt;dateStyle&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;timeStyle&lt;/code&gt; with individual part options throws an error, you can use one or the other but not both.&lt;/p&gt;&lt;p&gt;You might have seen them in the interactive example, but there&amp;#x27;s a few more options that are worth mentioning:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;hour12&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// override the locale&amp;#x27;s 12/24-hour preference&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;timeZone&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;UTC&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Europe/Amsterdam&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// specify a time zone&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;timeZoneName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;quot;CET&amp;quot;, &amp;quot;Central European Time&amp;quot;, etc.&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;dayPeriod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;narrow&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;quot;in the morning&amp;quot;, &amp;quot;AM&amp;quot;, etc.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;formattoparts&quot;&gt;formatToParts()&lt;/h4&gt;&lt;p&gt;All &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; formatters that have a &lt;code class=&quot;language-text&quot;&gt;.format()&lt;/code&gt; method also have a &lt;code class=&quot;language-text&quot;&gt;.formatToParts()&lt;/code&gt; method. Instead of returning one big string, it returns an array of &lt;code class=&quot;language-text&quot;&gt;{ type, value }&lt;/code&gt; objects. This makes it easy to wrap individual parts in markup for styling, such as making the day bold or adding a tooltip to the time zone name.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;dateStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;timeStyle&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatToParts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;2026-04-07T16:45:00Z&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;month&amp;#x27;,     value: &amp;#x27;April&amp;#x27; },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;,   value: &amp;#x27; &amp;#x27;     },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;day&amp;#x27;,       value: &amp;#x27;7&amp;#x27;     },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;,   value: &amp;#x27;, &amp;#x27;    },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;year&amp;#x27;,      value: &amp;#x27;2026&amp;#x27;  },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;,   value: &amp;#x27; at &amp;#x27;  },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;hour&amp;#x27;,      value: &amp;#x27;4&amp;#x27;     },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;,   value: &amp;#x27;:&amp;#x27;     },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;minute&amp;#x27;,    value: &amp;#x27;45&amp;#x27;    },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;,   value: &amp;#x27; &amp;#x27;     },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;dayPeriod&amp;#x27;, value: &amp;#x27;PM&amp;#x27;    },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;intlrelativetimeformat&quot;&gt;Intl.RelativeTimeFormat&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.RelativeTimeFormat&lt;/code&gt; gives you human-friendly relative time strings like &amp;quot;yesterday&amp;quot; and &amp;quot;in 3 days&amp;quot;.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Value (negative = past)&lt;/label&gt;&lt;input type=&quot;number&quot; value=&quot;-3&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Unit&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;second&quot;&gt;second&lt;/option&gt;&lt;option value=&quot;minute&quot;&gt;minute&lt;/option&gt;&lt;option value=&quot;hour&quot;&gt;hour&lt;/option&gt;&lt;option value=&quot;day&quot; selected=&quot;&quot;&gt;day&lt;/option&gt;&lt;option value=&quot;week&quot;&gt;week&lt;/option&gt;&lt;option value=&quot;month&quot;&gt;month&lt;/option&gt;&lt;option value=&quot;quarter&quot;&gt;quarter&lt;/option&gt;&lt;option value=&quot;year&quot;&gt;year&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;numeric&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;auto&quot; selected=&quot;&quot;&gt;auto&lt;/option&gt;&lt;option value=&quot;always&quot;&gt;always&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.RelativeTimeFormat(undefined, {
  numeric: &amp;quot;auto&amp;quot;
});
formatter.format(-3, &amp;quot;day&amp;quot;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;3 days ago&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;3 days ago&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;3 dagen geleden&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;il y a 3 jours&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;3 日前&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;قبل ٣ أيام&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Unlike &lt;code class=&quot;language-text&quot;&gt;DateTimeFormat&lt;/code&gt;, it works with a &lt;strong&gt;number and a unit&lt;/strong&gt;, not a &lt;code class=&quot;language-text&quot;&gt;Date&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relative &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RelativeTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;numeric&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;auto&amp;#x27; | &amp;#x27;always&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

relative&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;month&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;3 maanden geleden&amp;quot;&lt;/span&gt;
relative&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;day&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;morgen&amp;quot;&lt;/span&gt;
relative&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;day&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;gisteren&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With &lt;code class=&quot;language-text&quot;&gt;numeric: &amp;#x27;auto&amp;#x27;&lt;/code&gt;, the formatter uses words like &amp;quot;tomorrow&amp;quot; and &amp;quot;yesterday&amp;quot; when it can. With &lt;code class=&quot;language-text&quot;&gt;numeric: &amp;#x27;always&amp;#x27;&lt;/code&gt;, it always uses a number: &amp;quot;in 1 day&amp;quot;, &amp;quot;1 day ago&amp;quot;.&lt;/p&gt;&lt;p&gt;Units can be: &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;second&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;minute&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;hour&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;day&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;week&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;month&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;quarter&amp;#x27;&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;year&amp;#x27;&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Since you need to calculate the difference yourself, here&amp;#x27;s a helper pattern for calculating days between two dates:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;2026-05-01T00:00:00Z&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; diffInMs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; date &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; diffInDays &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diffInMs &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relative &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RelativeTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;numeric&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
relative&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;diffInDays&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;day&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;in 24 days&amp;quot; (or however far away May 1st is)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dates are in milliseconds. So you can convert it by dividing it to the unit you want. Instead of a really big number, like &lt;code class=&quot;language-text&quot;&gt;86400000&lt;/code&gt; for the number of milliseconds in a day, your code is easier to read if you do it step by step: 1000 milliseconds in a second, 60 seconds in a minute, 60 minutes in an hour and then 24 hours in a day.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;RelativeTimeFormat&lt;/code&gt; also has &lt;code class=&quot;language-text&quot;&gt;.formatToParts()&lt;/code&gt;, which breaks the output into &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;integer&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;literal&amp;#x27;&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/code&gt; pieces. This lets you wrap each part and style it separately.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relative &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RelativeTimeFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;numeric&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

relative&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatToParts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;month&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;integer&amp;#x27;, value: &amp;#x27;3&amp;#x27;, unit: &amp;#x27;month&amp;#x27; },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;literal&amp;#x27;, value: &amp;#x27; months ago&amp;#x27; },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;intldurationformat&quot;&gt;Intl.DurationFormat&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.DurationFormat&lt;/code&gt; formats time durations.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;style&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;long&quot; selected=&quot;&quot;&gt;long&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;option value=&quot;digital&quot;&gt;digital&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Hours&lt;/label&gt;&lt;input type=&quot;number&quot; min=&quot;0&quot; value=&quot;2&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Minutes&lt;/label&gt;&lt;input type=&quot;number&quot; min=&quot;0&quot; max=&quot;59&quot; value=&quot;45&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Seconds&lt;/label&gt;&lt;input type=&quot;number&quot; min=&quot;0&quot; max=&quot;59&quot; value=&quot;30&quot;/&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.DurationFormat(undefined, {
  style: &amp;quot;long&amp;quot;
});
formatter.format({
  &amp;quot;hours&amp;quot;: 2,
  &amp;quot;minutes&amp;quot;: 45,
  &amp;quot;seconds&amp;quot;: 30
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2 hours, 45 minutes, 30 seconds&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2 hours, 45 minutes, 30 seconds&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2 uur, 45 minuten en 30 seconden&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2 heures, 45 minutes et 30 secondes&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;2 時間 45 分 30 秒&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;ساعتان، و٤٥ دقيقة، و٣٠ ثانية&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;It lets you choose between an output like &lt;code class=&quot;language-text&quot;&gt;2:05:30&lt;/code&gt; and one like &amp;quot;2 hours, 5 minutes, 30 seconds&amp;quot;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; duration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DurationFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;long&amp;#x27; | &amp;#x27;short&amp;#x27; | &amp;#x27;narrow&amp;#x27; | &amp;#x27;digital&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

duration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;2 hours, 5 minutes, 30 seconds&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; clock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DurationFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;digital&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
clock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;2:05:30&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While the other date and time formatters use options to control which parts of the date to show, by default the &lt;code class=&quot;language-text&quot;&gt;DurationFormat&lt;/code&gt; just shows all the parts you give it in the formatter. So if you pass it hours, minutes and seconds, it will show all three. If you only pass it minutes and seconds, it will only show those two.&lt;/p&gt;&lt;p&gt;You can however force it to always show certain parts, even if they&amp;#x27;re zero by setting the *Display options:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;yearsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;monthsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;daysDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;hoursDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;minutesDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;secondsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;millisecondsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;microsecondsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;nanosecondsDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;auto&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;always&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;auto&lt;/code&gt; will only show the part if it&amp;#x27;s specified in the object, while &lt;code class=&quot;language-text&quot;&gt;always&lt;/code&gt; will always show it even if it&amp;#x27;s omitted.&lt;/p&gt;&lt;p&gt;The default option for all of these is almost always auto, except when you set &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;digital&lt;/code&gt;, in which case the default for hours, minutes and seconds becomes &lt;code class=&quot;language-text&quot;&gt;always&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;When you use &lt;code class=&quot;language-text&quot;&gt;digital&lt;/code&gt;, hours, minutes, and seconds are all shown by default even if they&amp;#x27;re zero, so you get &lt;code class=&quot;language-text&quot;&gt;00:45:00&lt;/code&gt; rather than just &lt;code class=&quot;language-text&quot;&gt;45&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;You can also format much finer durations, down to nanoseconds, which is useful for displaying performance measurements:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; perf &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DurationFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;narrow&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
perf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;423&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;microseconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;195&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;423ms 195μs&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;numbers--currencies&quot;&gt;Numbers &amp;amp; Currencies&lt;/h2&gt;&lt;h3 id=&quot;intlnumberformat&quot;&gt;Intl.NumberFormat&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.NumberFormat&lt;/code&gt; formats plain numbers, currencies, and units.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Number&lt;/label&gt;&lt;input type=&quot;number&quot; value=&quot;1234567.89&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;style&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;decimal&quot; selected=&quot;&quot;&gt;decimal&lt;/option&gt;&lt;option value=&quot;percent&quot;&gt;percent&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;notation&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;standard&quot; selected=&quot;&quot;&gt;standard&lt;/option&gt;&lt;option value=&quot;compact&quot;&gt;compact&lt;/option&gt;&lt;option value=&quot;scientific&quot;&gt;scientific&lt;/option&gt;&lt;option value=&quot;engineering&quot;&gt;engineering&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;useGrouping&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;auto&quot; selected=&quot;&quot;&gt;auto&lt;/option&gt;&lt;option value=&quot;always&quot;&gt;always&lt;/option&gt;&lt;option value=&quot;min2&quot;&gt;min2&lt;/option&gt;&lt;option value=&quot;false&quot;&gt;false&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.NumberFormat(undefined, {
  style: &amp;quot;decimal&amp;quot;,
  notation: &amp;quot;standard&amp;quot;,
  useGrouping: &amp;quot;auto&amp;quot;
});
formatter.format(1234567.89);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;1,234,567.89&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;1,234,567.89&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;1.234.567,89&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;1 234 567,89&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;1,234,567.89&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;١٬٢٣٤٬٥٦٧٫٨٩&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Those are all the same API, but they solve different problems, so it helps to keep them separate.&lt;/p&gt;&lt;h4 id=&quot;plain-numbers-and-percentages&quot;&gt;Plain numbers and percentages&lt;/h4&gt;&lt;p&gt;For plain numbers, you normally use the default &lt;code class=&quot;language-text&quot;&gt;decimal&lt;/code&gt; style. That gives you locale-aware grouping and decimal separators without any extra work.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1.000.000&amp;quot;&lt;/span&gt;
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123456.789&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;123.456,789&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Even for plain numbers, locale matters: Dutch uses &lt;code class=&quot;language-text&quot;&gt;.&lt;/code&gt; for thousands and &lt;code class=&quot;language-text&quot;&gt;,&lt;/code&gt; for decimals, while US English does the opposite.&lt;/p&gt;&lt;p&gt;Percentages use the same API with &lt;code class=&quot;language-text&quot;&gt;style: &amp;#x27;percent&amp;#x27;&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pct &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;fr-FR&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;percent&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
pct&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;10 %&amp;quot;&lt;/span&gt;
pct&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.753&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;75 %&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The general number formatter also supports different notations, which lets you set compact numbers like &lt;code class=&quot;language-text&quot;&gt;1M&lt;/code&gt; :&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compact &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;notation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;compact&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;compactDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

compact&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1007800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1M&amp;quot;&lt;/span&gt;
compact&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1534&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1.5K&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// For a longer version, use &amp;#x27;long&amp;#x27; instead of &amp;#x27;short&amp;#x27;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compactLong &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;notation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;compact&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;compactDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

compactLong&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1007800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1 million&amp;quot;&lt;/span&gt;
compactLong&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1534&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1.5 thousand&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note how the compact notation rounds the number to fit the format. 1,007,800 becomes &amp;quot;1M&amp;quot;, and 1,534 becomes &amp;quot;1.5K&amp;quot;. If you want to control the rounding, you can use the fraction digit options or switch to significant digits.&lt;/p&gt;&lt;p&gt;You can control how many decimal places appear with the fraction digit options, or switch to significant digits for scientific or measurement data:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Always show exactly two decimal places&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; price &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;minimumFractionDigits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;maximumFractionDigits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

price&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9.9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;9.90&amp;quot;&lt;/span&gt;
price&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9.999&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;10.00&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Cap to three significant digits&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; approx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;maximumSignificantDigits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

approx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123456&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;123,000&amp;quot;&lt;/span&gt;
approx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.001234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;0.00123&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;currencies&quot;&gt;Currencies&lt;/h4&gt;&lt;p&gt;A big feature in the number formatting API is formatting currencies, which even in its simplest form has quite a few differences between countries like if the symbol goes before or after the number, and getting them wrong in your UI will easily make your site look unprofessional or untrustworthy.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Amount&lt;/label&gt;&lt;input type=&quot;number&quot; value=&quot;123456.789&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Currency&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;USD&quot;&gt;USD&lt;/option&gt;&lt;option value=&quot;EUR&quot; selected=&quot;&quot;&gt;EUR&lt;/option&gt;&lt;option value=&quot;GBP&quot;&gt;GBP&lt;/option&gt;&lt;option value=&quot;JPY&quot;&gt;JPY&lt;/option&gt;&lt;option value=&quot;CNY&quot;&gt;CNY&lt;/option&gt;&lt;option value=&quot;BRL&quot;&gt;BRL&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;currencyDisplay&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;symbol&quot; selected=&quot;&quot;&gt;symbol&lt;/option&gt;&lt;option value=&quot;narrowSymbol&quot;&gt;narrowSymbol&lt;/option&gt;&lt;option value=&quot;code&quot;&gt;code&lt;/option&gt;&lt;option value=&quot;name&quot;&gt;name&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.NumberFormat(undefined, {
  style: &amp;quot;currency&amp;quot;,
  currency: &amp;quot;EUR&amp;quot;,
  currencyDisplay: &amp;quot;symbol&amp;quot;
});
formatter.format(123456.789);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;€123,456.79&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;€123,456.79&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;€ 123.456,79&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;123 456,79 €&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;€123,456.79&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;‏١٢٣٬٤٥٦٫٧٩ €&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Currency formatting is more than putting a symbol in front of a number. The symbol position, thousands separator, decimal separator, and rounding rules all vary by locale even when using the same currency (see how different euros are formatted compared between the Netherlands and France). &lt;code class=&quot;language-text&quot;&gt;Intl.NumberFormat&lt;/code&gt; handles all of that for you without you having to know the specifics across locales.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; moneyNL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;EUR&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; moneyFr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;fr-FR&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;EUR&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

moneyNL&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123456.789&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;€ 123.456,79&amp;quot;&lt;/span&gt;
moneyFr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123456.789&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;123 456,79 €&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also customize how the currency itself is displayed, such as showing the currency code instead of the symbol or even the full, &lt;em&gt;localized&lt;/em&gt; name of the currency:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;USD&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currencyDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;name&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;symbol&amp;#x27; | &amp;#x27;narrowSymbol&amp;#x27; | &amp;#x27;code&amp;#x27; | &amp;#x27;name&amp;#x27;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currencySign&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;accounting&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;standard&amp;#x27; (default) | &amp;#x27;accounting&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123456.789&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;123.456,79 Amerikaanse dollar&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The difference between &lt;code class=&quot;language-text&quot;&gt;symbol&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;narrowSymbol&lt;/code&gt; is that &lt;code class=&quot;language-text&quot;&gt;narrowSymbol&lt;/code&gt; can be &amp;quot;lossy&amp;quot;. For example, the symbol for US dollars is US$ because other countries also use dollars and it needs to differentiate between US dollars and, say, Canadian dollars, which is CA$. The narrow symbol for both is just $, which is more compact and works when you operate in a single country, but can be confusing if you have an international audience.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;currencySign&lt;/code&gt; controls how negative currency values are displayed. Normal people expect a minus sign in front of the number, but accountants, non-conformists that they are, prefer to put negative numbers in parentheses.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; standard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;USD&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currencySign&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;standard&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; accounting &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;USD&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currencySign&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;accounting&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

standard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123.45&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;-$123.45&amp;quot;&lt;/span&gt;

accounting&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123.45&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;($123.45)&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;units&quot;&gt;Units&lt;/h4&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;unit&lt;/code&gt; style is one of the most underused parts of &lt;code class=&quot;language-text&quot;&gt;Intl.NumberFormat&lt;/code&gt;. It formats a number with a physical unit (or units!) in a locale-appropriate way:&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Value&lt;/label&gt;&lt;input type=&quot;number&quot; value=&quot;100&quot;/&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Unit&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;kilometer&quot;&gt;kilometer&lt;/option&gt;&lt;option value=&quot;mile&quot;&gt;mile&lt;/option&gt;&lt;option value=&quot;meter&quot;&gt;meter&lt;/option&gt;&lt;option value=&quot;foot&quot;&gt;foot&lt;/option&gt;&lt;option value=&quot;kilometer-per-hour&quot; selected=&quot;&quot;&gt;kilometer-per-hour&lt;/option&gt;&lt;option value=&quot;mile-per-hour&quot;&gt;mile-per-hour&lt;/option&gt;&lt;option value=&quot;liter&quot;&gt;liter&lt;/option&gt;&lt;option value=&quot;gallon&quot;&gt;gallon&lt;/option&gt;&lt;option value=&quot;gram&quot;&gt;gram&lt;/option&gt;&lt;option value=&quot;kilogram&quot;&gt;kilogram&lt;/option&gt;&lt;option value=&quot;pound&quot;&gt;pound&lt;/option&gt;&lt;option value=&quot;celsius&quot;&gt;celsius&lt;/option&gt;&lt;option value=&quot;fahrenheit&quot;&gt;fahrenheit&lt;/option&gt;&lt;option value=&quot;year&quot;&gt;year&lt;/option&gt;&lt;option value=&quot;month&quot;&gt;month&lt;/option&gt;&lt;option value=&quot;day&quot;&gt;day&lt;/option&gt;&lt;option value=&quot;hour&quot;&gt;hour&lt;/option&gt;&lt;option value=&quot;minute&quot;&gt;minute&lt;/option&gt;&lt;option value=&quot;second&quot;&gt;second&lt;/option&gt;&lt;option value=&quot;gigabyte&quot;&gt;gigabyte&lt;/option&gt;&lt;option value=&quot;megabyte&quot;&gt;megabyte&lt;/option&gt;&lt;option value=&quot;kilobyte&quot;&gt;kilobyte&lt;/option&gt;&lt;option value=&quot;byte&quot;&gt;byte&lt;/option&gt;&lt;option value=&quot;megabit-per-second&quot;&gt;megabit-per-second&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;unitDisplay&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;short&quot; selected=&quot;&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;long&quot;&gt;long&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.NumberFormat(undefined, {
  style: &amp;quot;unit&amp;quot;,
  unit: &amp;quot;kilometer-per-hour&amp;quot;,
  unitDisplay: &amp;quot;short&amp;quot;
});
formatter.format(100);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;100 km/h&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;100 km/h&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;100 km/u&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;100 km/h&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;100 km/h&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;١٠٠ كم/س&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Supported units cover length, mass, temperature, speed, volume, digital size, and time. To get all of them, run &lt;code class=&quot;language-text&quot;&gt;Intl.supportedValuesOf(&amp;quot;unit&amp;quot;)&lt;/code&gt; in your console.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; boiling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;celsius&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unitDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;short&amp;#x27; | &amp;#x27;long&amp;#x27; | &amp;#x27;narrow&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

boiling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;100°C&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;celsius&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unitDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;100 graden Celsius&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The unit style also lets you combine any two units together when you need to display something like &amp;quot;miles per hour&amp;quot; or &amp;quot;kilometers per hour&amp;quot;. The notation for that is &lt;code class=&quot;language-text&quot;&gt;unit1-per-unit2&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; speed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;kilometer-per-hour&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unitDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;short&amp;#x27; | &amp;#x27;long&amp;#x27; | &amp;#x27;narrow&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

speed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;100 km/u&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;nl-NL&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;kilometer-per-hour&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;unitDisplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;100 kilometer per uur&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&quot;breaking-the-output-into-parts&quot;&gt;Breaking the output into parts&lt;/h4&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;NumberFormat&lt;/code&gt; also has &lt;code class=&quot;language-text&quot;&gt;.formatToParts()&lt;/code&gt;. It&amp;#x27;s particularly useful for currencies, where you might want to wrap the different parts of the number and style them:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; money &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NumberFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;currency&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;USD&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

money&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatToParts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1234.56&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;currency&amp;#x27;, value: &amp;#x27;$&amp;#x27;   },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;integer&amp;#x27;,  value: &amp;#x27;1&amp;#x27;   },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;group&amp;#x27;,    value: &amp;#x27;,&amp;#x27;   },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;integer&amp;#x27;,  value: &amp;#x27;234&amp;#x27; },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;decimal&amp;#x27;,  value: &amp;#x27;.&amp;#x27;   },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     { type: &amp;#x27;fraction&amp;#x27;, value: &amp;#x27;56&amp;#x27;  },&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;lists--text&quot;&gt;Lists &amp;amp; Text&lt;/h2&gt;&lt;p&gt;There are two APIs for working with natural language text: one for formatting lists and one for finding plural forms.&lt;/p&gt;&lt;h3 id=&quot;intllistformat&quot;&gt;Intl.ListFormat&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.ListFormat&lt;/code&gt; turns arrays into natural-language lists.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;type&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;conjunction&quot; selected=&quot;&quot;&gt;conjunction&lt;/option&gt;&lt;option value=&quot;disjunction&quot;&gt;disjunction&lt;/option&gt;&lt;option value=&quot;unit&quot;&gt;unit&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;style&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;long&quot; selected=&quot;&quot;&gt;long&lt;/option&gt;&lt;option value=&quot;short&quot;&gt;short&lt;/option&gt;&lt;option value=&quot;narrow&quot;&gt;narrow&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot; style=&quot;flex:2 1 220px&quot;&gt;&lt;label&gt;Items (one per line)&lt;/label&gt;&lt;textarea&gt;Apples
Oranges
Bananas&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const formatter = new Intl.ListFormat(undefined, {
  type: &amp;quot;conjunction&amp;quot;,
  style: &amp;quot;long&amp;quot;
});
formatter.format([
  &amp;quot;Apples&amp;quot;,
  &amp;quot;Oranges&amp;quot;,
  &amp;quot;Bananas&amp;quot;
]);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges, and Bananas&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges, and Bananas&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (UK)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-GB&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges and Bananas&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges en Bananas&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges et Bananas&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Spanish&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;es-ES&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;Apples, Oranges y Bananas&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When you need to turn an array into a human-readable list, use it instead of &lt;code class=&quot;language-text&quot;&gt;Array.join(&amp;#x27;, &amp;#x27;)&lt;/code&gt;. A comma-joined list is usually not how lists work in natural language. When you list things, you don&amp;#x27;t say &amp;quot;a, b, c&amp;quot;, you say &amp;quot;a, b &lt;em&gt;and&lt;/em&gt; c&amp;quot;. ListFormat handles that for you, so you don&amp;#x27;t have to break out an Array.reduce and keep track of whether you&amp;#x27;re on the last item or not.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✗ Not natural language&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Apples&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Oranges&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Bananas&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;, &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Apples, Oranges, Bananas&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ✓ Natural language&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formatter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;conjunction&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
formatter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Apples&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Oranges&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Bananas&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Apples, Oranges, and Bananas&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The three list types each fit a different kind of UI copy.&lt;/p&gt;&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;conjunction&lt;/code&gt; when the items belong together and you mean &amp;quot;and&amp;quot;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;conjunction&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Cowboy&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Hat&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Horse&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Cowboy, Hat, and Horse&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is what you want for summaries like supported features or anything else where all listed items apply.&lt;/p&gt;&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;disjunction&lt;/code&gt; when the items are alternatives and you mean &amp;quot;or&amp;quot;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;disjunction&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Cowboy&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Hat&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Horse&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Cowboy, Hat, or Horse&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works for option lists, fallback choices, or messaging like &amp;quot;Choose email, SMS, or push notifications.&amp;quot;&lt;/p&gt;&lt;p&gt;Use &lt;code class=&quot;language-text&quot;&gt;unit&lt;/code&gt; when you want a compact list without an explicit conjunction:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ListFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;unit&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Cowboy&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Hat&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Horse&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;Cowboy, Hat, Horse&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is useful for tight UI labels, specs, measurements or lists of ingredients.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt; option (&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;long&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;short&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;narrow&amp;#x27;&lt;/code&gt;) affects how the connector words are shown. In English, &lt;code class=&quot;language-text&quot;&gt;short&lt;/code&gt; uses an ampersand: &amp;quot;Cowboy, Hat, &amp;amp; Horse&amp;quot;.&lt;/p&gt;&lt;p&gt;In American English (&lt;code class=&quot;language-text&quot;&gt;en-US&lt;/code&gt;), a conjunction list uses the Oxford comma (&amp;quot;...Hat&lt;strong&gt;,&lt;/strong&gt; and Horse&amp;quot;). Switch to British English (&lt;code class=&quot;language-text&quot;&gt;en-GB&lt;/code&gt;) and the Oxford comma disappears. So you can stop arguing about the Oxford comma in your team and just let the browser handle it for you.&lt;/p&gt;&lt;h3 id=&quot;intlpluralrules&quot;&gt;Intl.PluralRules&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.PluralRules&lt;/code&gt; tells you which plural form to use for a given number. A plural form is the &amp;quot;s&amp;quot; when you go from &amp;quot;1 bird&amp;quot; to &amp;quot;2 birds&amp;quot;, but it can be more complex than that in other languages.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;type&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;cardinal&quot; selected=&quot;&quot;&gt;cardinal&lt;/option&gt;&lt;option value=&quot;ordinal&quot;&gt;ordinal&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;Number&lt;/label&gt;&lt;input type=&quot;number&quot; value=&quot;1&quot;/&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const count = 1;
const rules = new Intl.PluralRules(undefined, { type: &amp;#x27;cardinal&amp;#x27; });
rules.select(count);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;one&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;one&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;one&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;French&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;fr-FR&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;one&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;other&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValue--4Po-S&quot;&gt;one&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;What&amp;#x27;s weird about PluralRules is that, unlike the other formatting APIs, it doesn&amp;#x27;t actually do the formatting, it just gives you consistent categorization for the plural forms in the language.&lt;/p&gt;&lt;p&gt;So for bird/birds, it would tell you that for 1 bird the plural form is &amp;quot;one&amp;quot;, and for 0, 2, 3, etc. the plural form is &amp;quot;other&amp;quot;. You can then use that information to display the correct word form in your UI: &amp;quot;one&amp;quot; gets nothing appended, and &amp;quot;other&amp;quot; gets an &amp;quot;s&amp;quot;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; plurals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PluralRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

plurals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;other&amp;quot;&lt;/span&gt;
plurals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;one&amp;quot;&lt;/span&gt;
plurals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;other&amp;quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; label &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;count&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; bird&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;plurals&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;other&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;s&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;5 birds&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;quot;Cardinal&amp;quot; is the default plural type, and it basically means &amp;quot;count&amp;quot;. English has just two cardinal plural forms: &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;one&amp;#x27;&lt;/code&gt; (for the number 1) and &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;other&amp;#x27;&lt;/code&gt; (everything else, including 0). Other languages have more. Arabic, for example, has &lt;strong&gt;six&lt;/strong&gt; plural forms: &lt;code class=&quot;language-text&quot;&gt;zero&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;one&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;two&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;few&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;many&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;other&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Along with cardinal, you also have &lt;code class=&quot;language-text&quot;&gt;ordinal&lt;/code&gt;, which is used for ranking or ordering things: &amp;quot;1st&amp;quot;, &amp;quot;2nd&amp;quot;, &amp;quot;3rd&amp;quot;, &amp;quot;4th&amp;quot;, etc. In English, the plural forms for ordinals are &lt;code class=&quot;language-text&quot;&gt;one&lt;/code&gt; (for 1), &lt;code class=&quot;language-text&quot;&gt;two&lt;/code&gt; (for 2), &lt;code class=&quot;language-text&quot;&gt;few&lt;/code&gt; (for 3), and &lt;code class=&quot;language-text&quot;&gt;other&lt;/code&gt; (for everything else). So you can use that to get the correct suffix for any number:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PluralRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en-US&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;ordinal&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; suffixes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;st&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;two&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;nd&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;few&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;rd&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;th&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;ordinal&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;n&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;suffixes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;1st&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;2nd&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;3rd&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;4th&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ordinal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → &amp;quot;21st&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see from these two examples, it&amp;#x27;s your job to provide the right suffix or, in the case of other language, the right word form.&lt;/p&gt;&lt;p&gt;For example, 3 in Dutch is &amp;quot;drie&amp;quot;, but 3rd is &amp;quot;derde&amp;quot;. You can&amp;#x27;t easily append a suffix to get from &amp;quot;drie&amp;quot; to &amp;quot;derde&amp;quot;, so you need to provide the full word forms for each plural category.&lt;/p&gt;&lt;h2 id=&quot;segmentation&quot;&gt;Segmentation&lt;/h2&gt;&lt;h3 id=&quot;intlsegmenter&quot;&gt;Intl.Segmenter&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Segmenter&lt;/code&gt; breaks text into meaningful segments.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;granularity&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;grapheme&quot;&gt;grapheme&lt;/option&gt;&lt;option value=&quot;word&quot; selected=&quot;&quot;&gt;word&lt;/option&gt;&lt;option value=&quot;sentence&quot;&gt;sentence&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const segmenter = new Intl.Segmenter(&amp;#x27;default&amp;#x27;, {
  granularity: &amp;quot;word&amp;quot;
});
const segments = Array.from(segmenter.segment(text));&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb examples-styles-module--comparisonFullWidth--2tMMw&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonSource--CDQOy&quot;&gt;How many words are in this sentence?&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValueSmall--1lByy&quot;&gt;7 word-like segments&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;How&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;many&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;are&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;sentence&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagPunct--XJ_bN&quot;&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Dutch&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;nl-NL&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonSource--CDQOy&quot;&gt;Hoeveel woorden zitten er in deze zin?&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValueSmall--1lByy&quot;&gt;7 word-like segments&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;Hoeveel&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;woorden&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;zitten&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;er&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;deze&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;zin&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagPunct--XJ_bN&quot;&gt;?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Japanese&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ja-JP&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonSource--CDQOy&quot;&gt;この文には何語ありますか？&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValueSmall--1lByy&quot;&gt;8 word-like segments&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;この&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;文&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;に&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;は&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;何&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;語&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ありま&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;すか&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagPunct--XJ_bN&quot;&gt;？&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Arabic&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;ar-EG&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonSource--CDQOy&quot;&gt;كم عدد الكلمات في هذه الجملة؟&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonValueSmall--1lByy&quot;&gt;6 word-like segments&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;كم&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;عدد&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;الكلمات&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;في&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;هذه&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagSpace--2o9Kc&quot;&gt;·&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;الجملة&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagPunct--XJ_bN&quot;&gt;؟&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--legend--AXvVB&quot;&gt;&lt;span&gt;&lt;strong style=&quot;color:dodgerblue&quot;&gt;Blue&lt;/strong&gt; = word-like&lt;/span&gt;&lt;span&gt;&lt;strong style=&quot;color:#888&quot;&gt;Gray dashed&lt;/strong&gt; = whitespace&lt;/span&gt;&lt;span&gt;&lt;strong style=&quot;color:#ff86c6&quot;&gt;Pink&lt;/strong&gt; = punctuation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Breaking up sentences into parts might sound simple, but it quickly becomes complicated when you consider all the edge cases in natural language. For example, how would you count the number of words in a sentence? You might be tempted to do this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;How many words are in this sentence?&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 7 ✓&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Splitting on a space and then calling .length works well for English and other western languages, but it relies on the assumption that words are separated by spaces. That is not a universal rule in natural language. It falls apart here:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;これは日本語のテキストです&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27; &amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 1 ✗&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Japanese doesn&amp;#x27;t use spaces between words. So splitting on a space doesn&amp;#x27;t work and you&amp;#x27;re out of luck.&lt;/p&gt;&lt;p&gt;But even &lt;code class=&quot;language-text&quot;&gt;String.length&lt;/code&gt; to get the number of characters isn&amp;#x27;t as straightforward as it seems. How long is this &lt;em&gt;single&lt;/em&gt; emoji in a string?&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;👨‍👩‍👧‍👦&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 11&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not one, but eleven! That&amp;#x27;s because &lt;code class=&quot;language-text&quot;&gt;.length&lt;/code&gt; doesn&amp;#x27;t count visible characters, it counts code units. And this emoji happens to be a ligature of four individual emojis joined by zero-width joiners, which are invisible characters that connect them together. So while you see one emoji, JavaScript sees eleven code units and &lt;code class=&quot;language-text&quot;&gt;.length&lt;/code&gt; returns 11.&lt;/p&gt;&lt;p&gt;Detecting sentences gets even trickier. Since formatted numbers, abbreviations, and contractions all contain punctuation, splitting on a period also doesn&amp;#x27;t always work:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;This article could be 100.000 words long if
 you don&amp;#x27;t look out. Do you really need
 that many examples?&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;.&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 3 ✗&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (splits on the period in &amp;quot;100.000&amp;quot; too)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Of course, some sentences might also end with a question mark or an exclamation point, so splitting on a period isn&amp;#x27;t a reliable way to count sentences either.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Segmenter&lt;/code&gt; handles all of these correctly and it has three different levels of granularity so you can get characters, words or sentences in a text string.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;grapheme&amp;#x27;&lt;/code&gt;&lt;/strong&gt; (default): user-perceived characters (what you see as a single character, even if it&amp;#x27;s made up of multiple code points)&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; graphemes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Segmenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;granularity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;grapheme&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;graphemes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;👨‍👩‍👧‍👦&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 1 ✓&lt;/span&gt;
Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;graphemes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;café&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 4 ✓&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;word&amp;#x27;&lt;/code&gt;&lt;/strong&gt;: words, plus non-word segments such as spaces and punctuation. Each segment has an &lt;code class=&quot;language-text&quot;&gt;isWordLike&lt;/code&gt; boolean to help you filter out the non-word segments.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; words &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Segmenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;ja-JP&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;granularity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;word&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;words&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;これは日本語のテキストです&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isWordLike&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 6 ✓ (the browser knows Japanese word boundaries)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;sentence&amp;#x27;&lt;/code&gt;&lt;/strong&gt;: complete sentences, with abbreviation and number handling:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sentences &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Segmenter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;granularity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;sentence&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sentences&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;I went to the dr. smith yesterday. How are you?&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;segment&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// → [&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     &amp;quot;I went to the dr. smith yesterday. &amp;quot;,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//     &amp;quot;How are you?&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//   ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One caveat: the sentence segmenter handles &lt;code class=&quot;language-text&quot;&gt;dr.&lt;/code&gt; as an abbreviation when followed by lowercase, but if the next word is capitalized (like you would expect for a proper name), it may be treated as a sentence boundary. The segmenter is smart, but not perfect.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;segment()&lt;/code&gt; method returns an iterable, which is why we wrap it in &lt;code class=&quot;language-text&quot;&gt;Array.from()&lt;/code&gt;. Each item is an object with at minimum:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;segment&lt;/code&gt;: the text of the segment&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;index&lt;/code&gt;: where it starts in the original string&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt;: the original string&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For the &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;word&amp;#x27;&lt;/code&gt; granularity, items also have a boolean property:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;isWordLike&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; if the segment is a word rather than whitespace or punctuation&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For more, check out our article on just &lt;a href=&quot;/blog/using-the-intl-segmenter-api/&quot;&gt;the Segmenter API&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;collation&quot;&gt;Collation&lt;/h2&gt;&lt;h3 id=&quot;intlcollator&quot;&gt;Intl.Collator&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Collator&lt;/code&gt; sorts strings in a locale-aware way.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--demo--31yX8&quot;&gt;&lt;div class=&quot;examples-styles-module--controls--xxOEf&quot;&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot;&gt;&lt;label&gt;sensitivity&lt;/label&gt;&lt;select&gt;&lt;option value=&quot;base&quot; selected=&quot;&quot;&gt;base&lt;/option&gt;&lt;option value=&quot;accent&quot;&gt;accent&lt;/option&gt;&lt;option value=&quot;case&quot;&gt;case&lt;/option&gt;&lt;option value=&quot;variant&quot;&gt;variant&lt;/option&gt;&lt;/select&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--field--2-R5S&quot; style=&quot;flex:2 1 150px&quot;&gt;&lt;label&gt;Items (space separated)&lt;/label&gt;&lt;input type=&quot;text&quot; value=&quot;ñ n o á A ß z&quot;/&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--codeExample--2f1LR&quot;&gt;&lt;div class=&quot;examples-styles-module--codeBlockWrap--1RJsi&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;examples-styles-module--copyButton--3JT35&quot;&gt;Copy JS&lt;/button&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code&gt;const items = [&amp;quot;ñ&amp;quot;,&amp;quot;n&amp;quot;,&amp;quot;o&amp;quot;,&amp;quot;á&amp;quot;,&amp;quot;A&amp;quot;,&amp;quot;ß&amp;quot;,&amp;quot;z&amp;quot;];
const collator = new Intl.Collator(undefined, {
  sensitivity: &amp;quot;base&amp;quot;
});
items.sort(collator.compare);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--output--2Zy9b&quot;&gt;&lt;div class=&quot;examples-styles-module--comparison--35iDb &quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;array.sort()&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;plain sort&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Your locale&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;English (US)&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;en-US&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Spanish&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;es-ES&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;German&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;de-DE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonItem--2HEiF&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonHeader--1pUyN&quot;&gt;&lt;div class=&quot;examples-styles-module--comparisonLocale--_7ep0&quot;&gt;Swedish&lt;/div&gt;&lt;div class=&quot;examples-styles-module--comparisonStatus--1eNfE&quot;&gt;sv-SE&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--tagList--1nvKV&quot;&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;á&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ñ&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;ß&lt;/span&gt;&lt;span class=&quot;examples-styles-module--tag--6F5wu examples-styles-module--tagWord--2E-K9&quot;&gt;z&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Built-in &lt;code class=&quot;language-text&quot;&gt;Array.sort()&lt;/code&gt; sorts strings by their Unicode code points, which doesn&amp;#x27;t match the alphabetical order humans expect in many languages:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;ñ&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;n&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;o&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&amp;#x27;n&amp;#x27;, &amp;#x27;o&amp;#x27;, &amp;#x27;ñ&amp;#x27;]  ✗  (ñ has a higher code point, sorts after z)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;es&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Spanish&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;ñ&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;n&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;o&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&amp;#x27;n&amp;#x27;, &amp;#x27;ñ&amp;#x27;, &amp;#x27;o&amp;#x27;]  ✓  (ñ correctly sits between n and o in Spanish)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This matters anywhere you show an index, a directory, or any other sorted list.&lt;/p&gt;&lt;p&gt;You might already know &lt;code class=&quot;language-text&quot;&gt;String.prototype.localeCompare()&lt;/code&gt;, which also does locale-aware string comparison. Under the hood it uses &lt;code class=&quot;language-text&quot;&gt;Intl.Collator&lt;/code&gt; too, but it creates and destroys a new collator instance every time you call it. This is pretty slow (remember, creating the formatter is the expensive part) so what you can do instead is set up a collator once and then reuse its &lt;code class=&quot;language-text&quot;&gt;compare&lt;/code&gt; method.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Works, but slow:&lt;/span&gt;
items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;localeCompare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Better: creates the collator once and reuses it&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Intl.Collator&lt;/code&gt; has a handful of useful options. The most important one is &lt;code class=&quot;language-text&quot;&gt;sensitivity&lt;/code&gt;, which controls what differences in characters are considered:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;base&amp;#x27;: only base letters differ (&amp;#x27;a&amp;#x27; ≠ &amp;#x27;b&amp;#x27;, but &amp;#x27;a&amp;#x27; = &amp;#x27;á&amp;#x27; = &amp;#x27;A&amp;#x27;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;accent&amp;#x27;: base letters and accents differ, but not case&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;case&amp;#x27;: base letters and case differ, but not accents&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &amp;#x27;variant&amp;#x27;: all differences matter (default for sort order)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;sensitivity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;base&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;a&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;á&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// → 0 (considered equal)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Other useful options:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;usage&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;sort&amp;#x27;&lt;/code&gt; (default) or &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;search&amp;#x27;&lt;/code&gt;. Use &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;sort&amp;#x27;&lt;/code&gt; for ordering lists. If you want to use collator to filter lists based on language-specific logic, you can use &lt;code class=&quot;language-text&quot;&gt;search&lt;/code&gt; and test if the result is &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; to check for a match. for the collator, &lt;code class=&quot;language-text&quot;&gt;cafe&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;café&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;CAFE&lt;/code&gt; are all considered equal with &lt;code class=&quot;language-text&quot;&gt;sensitivity: &amp;#x27;base&amp;#x27;&lt;/code&gt;, so using search means you have a more natural search experience for users in languages with accents and case differences.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;caseFirst&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;upper&amp;#x27;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;lower&amp;#x27;&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;&amp;#x27;false&amp;#x27;&lt;/code&gt; (default). Controls whether uppercase or lowercase sorts first when case differences matter.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ignorePunctuation&lt;/code&gt;: &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; (default). When &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;, punctuation differences are ignored during comparison.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;collation&lt;/code&gt;: requests a specific collation variant. Common values are:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;phonebk&amp;#x27;&lt;/code&gt;: phonebook-style sorting, used in German where ä sorts as ae, ö as oe, ü as ue&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;pinyin&amp;#x27;&lt;/code&gt;: pinyin ordering for Latin and CJK characters, common for Chinese&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;stroke&amp;#x27;&lt;/code&gt;: stroke-count ordering for CJK characters, used in Traditional Chinese&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;trad&amp;#x27;&lt;/code&gt;: traditional ordering, for example treating ch and ll as distinct letters in Spanish&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;emoji&amp;#x27;&lt;/code&gt;: recommended ordering for emoji characters&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;#x27;standard&amp;#x27;&lt;/code&gt;: the locale&amp;#x27;s standard ordering (the default)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can get the full list your browser supports with &lt;code class=&quot;language-text&quot;&gt;Intl.supportedValuesOf(&amp;#x27;collation&amp;#x27;)&lt;/code&gt;.&lt;/p&gt;&lt;h4 id=&quot;numeric-sorting&quot;&gt;Numeric sorting&lt;/h4&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;numeric&lt;/code&gt; option is worth a special mention. By default, string sorting is done character-by-character. That means &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt; sorts before &lt;code class=&quot;language-text&quot;&gt;9&lt;/code&gt; because &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; is less than &lt;code class=&quot;language-text&quot;&gt;9&lt;/code&gt;. This is almost never what you want when sorting anything with numbers in it.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;chapter10.txt&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;chapter9.txt&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;chapter2.txt&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;chapter1.txt&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Default sort per-character&lt;/span&gt;
files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&amp;#x27;chapter1.txt&amp;#x27;, &amp;#x27;chapter10.txt&amp;#x27;, &amp;#x27;chapter2.txt&amp;#x27;, &amp;#x27;chapter9.txt&amp;#x27;]  ✗&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Numeric sort&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;numeric&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → [&amp;#x27;chapter1.txt&amp;#x27;, &amp;#x27;chapter2.txt&amp;#x27;, &amp;#x27;chapter9.txt&amp;#x27;, &amp;#x27;chapter10.txt&amp;#x27;]  ✓&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This comes up frequently with file names, version numbers, and any list where users use numbers in names: &lt;code class=&quot;language-text&quot;&gt;image1&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;image2&lt;/code&gt;, … &lt;code class=&quot;language-text&quot;&gt;image10&lt;/code&gt;. Without &lt;code class=&quot;language-text&quot;&gt;numeric: true&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;image10&lt;/code&gt; sorts right after &lt;code class=&quot;language-text&quot;&gt;image1&lt;/code&gt; and before &lt;code class=&quot;language-text&quot;&gt;image2&lt;/code&gt;. With it, they sort the way users expect.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;When you first look at &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt;, it can feel like a loose collection of unrelated APIs. It does something with dates, currencies and also sorts lists or counts words? What an API! If you only look at the constructor names, it can seem like a lot to memorize.&lt;/p&gt;&lt;p&gt;But &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; is built on one core idea: you have a type of data, and you want an easy way to format it in natural language. You pick a locale. You pick some options. You create a formatter, parts formatter, comparator, or segmenter. Then you reuse it with your data. Whether you&amp;#x27;re formatting dates, numbers, currencies, lists, plurals, words, or sorted strings, the API shape stays similar.&lt;/p&gt;&lt;p&gt;Understanding that shared foundation makes the whole API much easier to reason about and easier to reach for when you come across a string formatting task. It means less hand-rolled formatting code, fewer dependencies, and fewer subtle bugs where a value looks correct to you but confusing or wrong to someone in another locale.&lt;/p&gt;&lt;p&gt;The browser already knows how this stuff works and did the hard work for you. &lt;code class=&quot;language-text&quot;&gt;Intl&lt;/code&gt; gives you a way to use that knowledge directly.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to debug the @starting-style at-rule in Polypane]]></title><description><![CDATA[The  @starting-style  at-rule makes it possible to animate in elements as you add them to the DOM, something that previously needed…]]></description><link>https://polypane.app/blog/how-to-debug-the-starting-style-at-rule-in-polypane/</link><guid isPermaLink="false">https://polypane.app/blog/how-to-debug-the-starting-style-at-rule-in-polypane/</guid><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule makes it possible to animate in elements as you add them to the DOM, something that previously needed JavaScript trickery to achieve. In this article, we&amp;#x27;ll go over how to use &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; and how to debug it in Polypane. Once you get the mental model, it becomes very easy to use, and lets you create smooth animations for elements as they are added to the page, without needing any JavaScript.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; is supported across Chromium, Firefox and Safari and has been since 2024. It&amp;#x27;s ready to use!&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Despite &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; being implemented since the very end of 2023 (in Chromium, which Polypane uses), debugging it became available only in the Chromium DevTools in version 143, released in November 2025. Polypane on the other hand, has supported inspecting, editing and debugging the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule since &lt;a href=&quot;/blog/polypane-20-browser-features-and-performance/#starting-style-support&quot;&gt;Polypane 20&lt;/a&gt; released all the way back in June of 2024, over a year and a half earlier.&lt;/p&gt;&lt;p&gt;Why did Polypane get support for this so much earlier? Because Polypane is built for web developers, not browser developers. That seems like splitting hairs, but it has a direct impact on how we approach devtools for new features. First, though, let&amp;#x27;s look at what the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule is and how it works.&lt;/p&gt;&lt;h2 id=&quot;the-problem-starting-style-solves&quot;&gt;The problem @starting-style solves&lt;/h2&gt;&lt;p&gt;When the browser animates something, it needs to know two things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Where to animate &lt;em&gt;from&lt;/em&gt;&lt;/li&gt;&lt;li&gt;Where to animate &lt;em&gt;to&lt;/em&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;When an element is already in the DOM, and you change its style, the browser can easily figure out both of these things. The &amp;quot;from&amp;quot; is the current state of the element, and the &amp;quot;to&amp;quot; is the resolved, computed new styles. Add a transition time and the browser can animate between the two.&lt;/p&gt;&lt;p&gt;When an element is &lt;em&gt;added&lt;/em&gt; to the DOM, however, there is no &amp;quot;from&amp;quot; state. There is nothing, and then there is something. The browser only knows the &amp;quot;to&amp;quot; state, the resolved styles of the element. The browser doesn&amp;#x27;t know where to animate from, so it just snaps the element into place without any animation.&lt;/p&gt;&lt;p&gt;Previously, you had to add the element hidden (opacity 0, height 0, off-screen), then reveal it after it was added to the DOM. This gives the browser a &amp;quot;from&amp;quot; and a &amp;quot;to&amp;quot; state to animate. Having to do any animation like this in two stages is a pain and it also means you needed JavaScript.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; solves this issue by letting you define that &amp;quot;from&amp;quot; state directly in your CSS.&lt;/p&gt;&lt;h2 id=&quot;how-to-add-a-starting-style&quot;&gt;How to add a starting style&lt;/h2&gt;&lt;p&gt;To add a starting style to an element, you use the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule in your CSS. It&amp;#x27;s easiest to use CSS Nesting for this. The syntax looks like this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; green&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 1s ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@starting-style&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &amp;quot;to&amp;quot; state is what you want the element to be styled as. In this case, a rounded green square. And the &amp;quot;from&amp;quot; state is the stuff in the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; block: the background color starts as red, the opacity starts at 0 and it&amp;#x27;s blurred. When you add this div to the DOM, it will transition from a blurred transparent red square to a solid green one.&lt;/p&gt;&lt;div class=&quot;StartingStyleDemo-styles-module--container--2yLtA&quot;&gt;&lt;div class=&quot;StartingStyleDemo-styles-module--row--1Wdwl&quot;&gt;&lt;/div&gt;&lt;button class=&quot;StartingStyleDemo-styles-module--button--1Rwji&quot;&gt;Add Box&lt;/button&gt;&lt;/div&gt;&lt;p&gt;I&amp;#x27;ll admit that an &amp;quot;at-rule&amp;quot; without anything after it looks a bit odd (like only writing &lt;code class=&quot;language-text&quot;&gt;@media&lt;/code&gt; without the media query), but that&amp;#x27;s because &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; doesn&amp;#x27;t need parameters.&lt;/p&gt;&lt;p&gt;You get used to it.&lt;/p&gt;&lt;h2 id=&quot;keeping-the-starting-style-separate-and-dealing-with-specificity&quot;&gt;Keeping the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; separate and dealing with specificity&lt;/h2&gt;&lt;p&gt;If you want, you can keep the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; separate as well:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; green&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 1s ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@starting-style&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;5px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you look at the above code sample you might be tempted to place the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; block &lt;em&gt;before&lt;/em&gt; the regular styles to make it clear you&amp;#x27;re going from those first styles to the regular styles. Unfortunately, that won&amp;#x27;t work. &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; itself has no specificity (like any at-rule), so the &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; selector inside it has the same &lt;a href=&quot;/css-specificity-calculator/?selector=div&quot;&gt;specificity&lt;/a&gt; as the &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; selector above it.&lt;/p&gt;&lt;p&gt;When specificity is the same, the last specified one wins, so if the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; was first, its styling would always be overwritten by the &amp;quot;to&amp;quot; styling from the declaration below it, and your animation would never play.&lt;/p&gt;&lt;p&gt;You define the &amp;quot;from&amp;quot; styles last to deal with specificity, and the browser makes sure to only apply those styles initially for the animation, and then remove them once the animation is done.&lt;/p&gt;&lt;h2 id=&quot;inspecting-and-debugging-the-starting-style&quot;&gt;Inspecting and debugging the starting style&lt;/h2&gt;&lt;p&gt;To debug the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule in Polypane, inspect the element by holding &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌥&lt;/kbd&gt; and clicking it. This opens up the elements panel with all the styles that apply to the element, including the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; styles. If you&amp;#x27;re reading this article in Polypane, try it with the demo above!&lt;/p&gt;&lt;video src=&quot;static/startingstyle2-7195950118e3d8ebce6b34a931858e9e.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;These styles can be edited and changed like any other styles, with changes applied directly just like editing any other CSS property.&lt;/p&gt;&lt;p&gt;Because they are only applied to the &amp;quot;from&amp;quot; part of the animation, to see them in action you have two options. For the demo above: press the button to add a new div with the updated &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt;:&lt;/p&gt;&lt;video src=&quot;static/startingstyle4-790578fe4f785e4311d375234cf71f18.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;Or force the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; from the Polypane elements panel, which you can do from the same menu that lets you force hover, focus and other element states:&lt;/p&gt;&lt;video src=&quot;static/startingstyle3-76c578ff29984efea6a30e2b26e306d6.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;With just those tools, you can easily inspect and debug your &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; styles, without needing anything more than the regular elements panel.&lt;/p&gt;&lt;p&gt;You might be wondering why this needs a tutorial, it&amp;#x27;s literally just &amp;quot;select element, edit styles, see changes&amp;quot;. That&amp;#x27;s kind of the point. It&amp;#x27;s that simple because of the design choices we made for the elements panel.&lt;/p&gt;&lt;h2 id=&quot;why-polypanes-elements-panel-makes-this-so-easy&quot;&gt;Why Polypane&amp;#x27;s Elements panel makes this so easy&lt;/h2&gt;&lt;p&gt;So what&amp;#x27;s different about Polypane that makes this so easy? It has everything to do with the design choices we made for the Elements panel.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Regular developer tools are built from the viewpoint of people building browsers.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you build a browser and the devtools to support it, you want to be able to use the elements panel to verify that the browser is showing the correct styles for an element. You want to be able to see the styles that apply to an element in its current state. So the elements panel need to show you the exact same thing the browser is showing you (and only that thing). The devtools implementation only cares about the current state, and that means the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; style declaration is not visible in your styles tab:&lt;/p&gt;&lt;img src=&quot;static/chromedevtools1-53543807c997edb12b128c7c6a507bce.png&quot; alt=&quot;The Chrome DevTools elements panel showing only the styles that apply to the current state of the element&quot;/&gt;&lt;p&gt;Anything that is not part of the page&amp;#x27;s current state is irrelevant if your elements panel is designed to verify the current state. This is why you also don&amp;#x27;t see &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; styles in any browser&amp;#x27;s devtools unless you force a hover state.&lt;/p&gt;&lt;p&gt;This works exactly how you want it to work when you&amp;#x27;re interested in confirming that the browser is rendering styles correctly. But it creates friction for the actual work of web development, where you constantly need to inspect, compare, and debug styles across different states. In the Chrome DevTools, you need to activate the @starting-style badge to see the starting styles:&lt;/p&gt;&lt;img src=&quot;static/chromedevtools2-e1c7e88aea89c5c3a2ad82b4b1eb15db.png&quot; alt=&quot;The Chrome DevTools elements panel showing only the styles that apply to the current state of the element&quot;/&gt;&lt;p&gt;Polypane&amp;#x27;s devtools are different. &lt;strong&gt;Polypane&amp;#x27;s developer tools are built from the viewpoint of web developers&lt;/strong&gt;. As a web developer, the current state of an element is &lt;strong&gt;just one of the states you care about&lt;/strong&gt;, just like you care about your design at all screen sizes, not just the one your screen happens to have.&lt;/p&gt;&lt;p&gt;Polypane&amp;#x27;s elements panel is designed to show you all the styles that apply to an element, even if those styles are for states your element currently isn&amp;#x27;t in, like &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;:active&lt;/code&gt; styles:&lt;/p&gt;&lt;img src=&quot;static/polystyles-75da454323d6921052a9c09944ebccf1.png&quot; alt=&quot;The Polypane elements panel showing styles for :hover and :active states, along with the regular style&quot;/&gt;&lt;p&gt;This means you can still inspect and edit them without needing to force them first. For the &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; at-rule, this means you can see all the styles that apply to the element and edit them directly.&lt;/p&gt;&lt;p&gt;In Polypane you can work with these styles directly and see the changes in real time.&lt;/p&gt;&lt;h3 id=&quot;safari-and-firefox-do-a-bit-of-both&quot;&gt;Safari and Firefox do a bit of both&lt;/h3&gt;&lt;p&gt;It should be noted that, despite only showing the current state of elements when it comes to states like &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;:focus&lt;/code&gt;, both Firefox and Safari do actually show &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; styles directly in the devtools!&lt;/p&gt;&lt;p&gt;While I appreciate that functionality, this inconsistency creates a confusing developer experience. Why show &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; but not &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt;? Both are non-current states. This means developers have to learn different workflows for different CSS features: you edit &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; one way, but &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; styles another way.&lt;/p&gt;&lt;h2 id=&quot;polypane-is-for-web-developers-not-browser-makers&quot;&gt;Polypane is for web developers, not browser makers&lt;/h2&gt;&lt;p&gt;Polypane&amp;#x27;s more consistent approach means you learn one workflow that works for all states. Whenever new features like this are added, my primary concern when implementing developer tooling is&lt;/p&gt;&lt;ol&gt;&lt;li&gt;What do web developers need to know to use this feature effectively?&lt;/li&gt;&lt;li&gt;How can I make it easy for web developers to understand the whole feature in the context of the rest of the page?&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The choice to show &lt;em&gt;all&lt;/em&gt; styles for an element regardless of state is a direct result of that. As a developer, you want an overview of the styles of an element, because comparing styles between states is a common task when debugging hover effects, animation or transitions.&lt;/p&gt;&lt;p&gt;Yes, if you&amp;#x27;ve spent years following the traditional browser devtools workflow, there&amp;#x27;s a small learning curve. But the payoff is immediate: you understand your own code better because everything is visible. You spend less time hunting through panels and forcing states.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s why Polypane had &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; support over a year before Chrome DevTools itself: The design choices we made for the elements panel made it the natural, obvious thing to do.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Polypane 28: Project improvements, Elements panel updates and Chromium 146]]></title><description><![CDATA[The first release of 2026 comes with plenty of improvements to Projects, the new Environments feature prevents you from confusing production…]]></description><link>https://polypane.app/blog/polypane-28-project-improvements-elements-panel-updates-and-chromium-146/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-28-project-improvements-elements-panel-updates-and-chromium-146/</guid><pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The first release of 2026 comes with plenty of improvements to Projects, the new Environments feature prevents you from confusing production for localhost, the Elements panel is faster and more powerful than ever, and Polypane now runs on Chromium 146.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;projects&quot;&gt;Projects&lt;/h2&gt;&lt;p&gt;We continue to get really good feedback and user requests on Projects, which helps us prioritize what to work on next. In Polypane 28, we&amp;#x27;re introducing a new Environments option, Bookmarks are now available in the Browse panel too and Projects, Bookmarks and Environments can now be re-ordered with Drag and Drop, so you get to decide what order they appear in.&lt;/p&gt;&lt;h3 id=&quot;project-environments&quot;&gt;Project Environments&lt;/h3&gt;&lt;p&gt;When working on any project, you probably have it running in multiple places: locally on your machine, on a staging server/acceptance environment and in production. And you switch between them depending on what you&amp;#x27;re working on.&lt;/p&gt;&lt;p&gt;And maybe more often than you&amp;#x27;d like to admit, you accidentally keep refreshing production in utter frustration because the changes you made on localhost aren&amp;#x27;t showing up. (yeah, absolutely never happens to me neither).&lt;/p&gt;&lt;p&gt;With the new Projects environments, not only is it incredibly easy to quickly switch between your different environments, the color indicators in the tab and address bar let you see at a glance which environment you&amp;#x27;re currently working in. For example, my localhost is always yellow:&lt;/p&gt;&lt;img src=&quot;static/yellowlocalhost-85af81833756a69d28d389386fddf336.png&quot; class=&quot;imgshadow&quot; alt=&quot;Add environment dialog&quot;/&gt;&lt;p&gt;Each environment can have its own color that is shown in the tab, the address bar and the environment switcher. At a glance you immediately know which environment you&amp;#x27;re working in, and you&amp;#x27;re never stuck refreshing production waiting for changes you&amp;#x27;ve made on localhost to show up.&lt;/p&gt;&lt;img src=&quot;static/addproject-f4cc7e914bd75ce9ef392bbfde06a4c7.png&quot; class=&quot;imgshadow&quot; alt=&quot;Add environment dialog&quot;/&gt;&lt;p&gt;Switching between environments makes it easy to keep the same context while testing your site in different setups: switching the environment will keep you on the same page, just on the different domain.&lt;/p&gt;&lt;p&gt;Switch to specific environments using the environment switcher, or middle click the icon to simply advance to the next environment in the list.&lt;/p&gt;&lt;img src=&quot;static/environments-d46c2869cb9c192d2e2d4951f7d5cc79.png&quot; class=&quot;imgshadow&quot; alt=&quot;Environments dropdown&quot;/&gt;&lt;p&gt;This feature has been on our backlog since early 2025 and having used it in the past few weeks I can say it&amp;#x27;s a much bigger productivity boost than I anticipated.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Want to try Environments?&lt;/strong&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Start your free 14-day trial&lt;/a&gt;. No credit card required.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;/docs/projects/#environments&quot;&gt;Learn more about Project Environments →&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;project-bookmarks-and-environment-reordering&quot;&gt;Project, bookmarks and environment reordering&lt;/h3&gt;&lt;p&gt;You can now reorder Projects, Bookmarks and Environments using drag and drop. Just grab the item you want to move and drag it to the desired position in the list.&lt;/p&gt;&lt;p&gt;For projects, this can be done right in the project switcher so you can put the projects with the highest priority on top.&lt;/p&gt;&lt;video src=&quot;static/sortprojects-694aec9508b3842671a9d26926f1b346.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Michael&lt;/strong&gt; for requesting project reordering!&lt;/aside&gt;&lt;p&gt;Bookmarks and environments can be reordered in the Project editor, again using drag and drop:&lt;/p&gt;&lt;video src=&quot;static/sortbookmarks-81fe84440e0ceef58fe93790e63dec85.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;h3 id=&quot;color-indicator-for-panes&quot;&gt;Color indicator for panes&lt;/h3&gt;&lt;p&gt;The color indicators for domain and tab overrides were only shown in the tab and address bar. In Polypane 28 we now also show this for any pane that has &lt;a href=&quot;/docs/showing-multiple-urls/#decoupling-panes-for-different-urls&quot;&gt;navigation sync&lt;/a&gt; disabled. When you&amp;#x27;re comparing multiple environments in a single tab, this makes it easy to see which environment you&amp;#x27;re currently viewing.&lt;/p&gt;&lt;img src=&quot;static/panecolor-d4f6428853181cfd8fdd55ab3e435b4e.png&quot; class=&quot;imgshadow&quot; alt=&quot;Pane environment colors&quot;/&gt;&lt;h3 id=&quot;be-inclusive-integration&quot;&gt;Be Inclusive integration&lt;/h3&gt;&lt;p&gt;Thr accessibility auditing tool &lt;a href=&quot;https://beinclusive.app&quot;&gt;Be Inclusive&lt;/a&gt; can now export audit reports as Polypane projects, making it super easy to import your audits into Polypane.&lt;/p&gt;&lt;p&gt;Export your audit from Be Inclusive and load it into Polypane. The target samples you specified in Be Inclusive are automatically added as bookmarks in your project, so you can quickly jump to those pages and test your fixes.&lt;/p&gt;&lt;img src=&quot;https://beinclusive.app/img/ogimage.png&quot; class=&quot;imgshadow&quot; alt=&quot;Be Inclusive&quot;/&gt;&lt;blockquote&gt;&lt;p&gt;Polypane subscribers get a &lt;strong&gt;15% discount on Be Inclusive&lt;/strong&gt;! If you have an active subscription, find a discount code on our &lt;a href=&quot;https://dashboard.polypane.app/deals&quot;&gt;Deals page&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;cli-options&quot;&gt;CLI options&lt;/h2&gt;&lt;p&gt;Polypane now has several new and improved command line options, including a completely new &lt;code class=&quot;language-text&quot;&gt;--help&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;-h&lt;/code&gt; overview that shows all available options:&lt;/p&gt;&lt;img src=&quot;static/polycli-93b97543d58ed69a24c1c647f783d2a4.png&quot; alt=&quot;Polypane CLI help output&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;p&gt;You can now open as many URLs as you want when starting Polypane from the command line. Just add them after any options and they will be opened in tabs in the current project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ polypane https://polypane.com/docs/ https://polypane.app/support/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can use &lt;code class=&quot;language-text&quot;&gt;--project&lt;/code&gt; to switch to a specific project when opening Polypane from the command line,&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ polypane &lt;span class=&quot;token parameter variable&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;projectname&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or you can open a URL directly in a specific project&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ polypane &lt;span class=&quot;token parameter variable&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;projectname https://polypane.com/docs/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Marc&lt;/strong&gt; for requesting this!&lt;/aside&gt;&lt;p&gt;When you try to open a project that doesn&amp;#x27;t exist, Polypane will now ask if you want to create it. You can also automatically create the project without prompt by adding the &lt;code class=&quot;language-text&quot;&gt;--create&lt;/code&gt; flag. That will create the project if it doesn&amp;#x27;t exist yet, and open any URLs in the newly created project.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ polypane &lt;span class=&quot;token parameter variable&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;projectname &lt;span class=&quot;token parameter variable&quot;&gt;--create&lt;/span&gt; https://polypane.com/docs/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Altogether this makes for an incredibly easy way to quickly instantiate new projects from the command line. You could even add this to your scaffolding scripts for new projects and automate the creation workflow entirely.&lt;/p&gt;&lt;p&gt;This new set of CLI options also makes it easy to integrate Polypane in your existing workflows, for example by &lt;a href=&quot;/blog/always-open-localhost-in-your-development-browser/&quot;&gt;always opening specific urls in a specific project&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/command-line-options/&quot;&gt;Learn more about CLI options →&lt;/a&gt;&lt;/p&gt;&lt;h3 id=&quot;traydock-options&quot;&gt;Tray/Dock options&lt;/h3&gt;&lt;p&gt;On macOS and Windows, you can now right-click the Polypane icon in the Dock/Tray to quickly open or switch to a specific project. This makes it easy to switch projects without having to open the project switcher first.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Benoit&lt;/strong&gt; for suggesting this improvement!&lt;/aside&gt;&lt;p&gt;This feature is not available on Linux because it&amp;#x27;s not possible to populate a .desktop file with dynamic menu items.&lt;/p&gt;&lt;h2 id=&quot;disabled-cache-logic-improvements&quot;&gt;Disabled cache logic improvements&lt;/h2&gt;&lt;p&gt;Polypane&amp;#x27;s cache disabling logic has been improved in Polypane 28. Instead of applying cache disabling to sessions, which could lead to unexpected cache handling in specific panes, we now apply cache disabling directly on the web content instances of individual panes.&lt;/p&gt;&lt;p&gt;What you will notice is that a disabled cache behaves more consistently across all panes and tabs, and that there are fewer instances of stale caching on normal reloads. With this change, cache disabling is more predictable and in line with the &amp;#x27;disable cache&amp;#x27; button in the Chrome devtools.&lt;/p&gt;&lt;h3 id=&quot;cache-is-still-enabled-by-default&quot;&gt;Cache is still enabled by default&lt;/h3&gt;&lt;p&gt;Polypane continues to have caching &lt;em&gt;enabled&lt;/em&gt; by default. I strongly think the Chromium Devtools feature that automatically disables caching is harming web developers more than it helps them.&lt;/p&gt;&lt;p&gt;It obscures real-world issues that only appear when caching is enabled, and it gives a false sense of security that your site works correctly when in reality it might be broken for your users.&lt;/p&gt;&lt;h2 id=&quot;console-panel-consolegroup-support&quot;&gt;Console panel: console.group support&lt;/h2&gt;&lt;p&gt;The Console panel now supports &lt;code class=&quot;language-text&quot;&gt;console.group&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;console.groupCollapsed&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;console.groupEnd&lt;/code&gt;, making it easier to structure your console output when debugging.&lt;/p&gt;&lt;img src=&quot;static/consolegroup-382367ccfbef0b6ad8615f818d5fec42.png&quot; class=&quot;imgshadow&quot; alt=&quot;Console group example&quot;/&gt;&lt;p&gt;Polypane&amp;#x27;s console is built from the ground up and shares no code with Chrome DevTools so we can support collecting console messages from all panes. Since we can&amp;#x27;t re-use existing code it took us a while to get this feature in while making sure that the console clearly shows where messages come from and consolidating groups across panes correctly.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Giles&lt;/strong&gt; for requesting this feature!&lt;/aside&gt;&lt;h2 id=&quot;elements-panel&quot;&gt;Elements panel&lt;/h2&gt;&lt;p&gt;When using the Elements panel in Polypane 28 you&amp;#x27;ll notice how much quicker it shows styles when inspecting elements. What took a few hundred ms now takes less than 50ms for the majority of pages. We&amp;#x27;ve made several performance improvements under the hood that make inspecting elements and switching between them much faster, especially on pages with a lot of styles.&lt;/p&gt;&lt;h3 id=&quot;editing-css-selectors&quot;&gt;Editing CSS selectors&lt;/h3&gt;&lt;p&gt;New in Polypane 28 is that you can click on any CSS selector in the Styles pane to edit it. This makes it easy to refine your selectors or re-apply styles to other elements by changing the selector to match those elements.&lt;/p&gt;&lt;video src=&quot;static/editselector-f49a80a6372524248fbb4739aa68e2e9.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;Other browser devtools also have this feature, but of course the Polypane developer tools apply the changes live across all panes, so you can see how your changes affect different viewports and devices immediately.&lt;/p&gt;&lt;h3 id=&quot;improved-important-visibility&quot;&gt;Improved &lt;code class=&quot;language-text&quot;&gt;!important&lt;/code&gt; visibility&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;!important&lt;/code&gt; css properties now have a new design that makes them much more visible than the previous design.&lt;/p&gt;&lt;img src=&quot;static/important-6aee5b040ec562907b764eb1680df371.png&quot; class=&quot;imgshadow&quot; alt=&quot;Important styling example&quot;/&gt;&lt;p&gt;In Polypane 28 both the property name and value have a different color when you use !important, making them much more visible in the list of styles. &lt;code class=&quot;language-text&quot;&gt;!important&lt;/code&gt; is best avoided of course, but sometimes it&amp;#x27;s the most practical solution. With this new style highlighting, figuring out what&amp;#x27;s going on with your styles is a bit easier.&lt;/p&gt;&lt;h3 id=&quot;notable-fixes-and-improvements&quot;&gt;Notable Fixes and improvements&lt;/h3&gt;&lt;p&gt;Editing styles on parents nodes were not being applied in Polypane 27.x. This has been fixed in Polypane 28, so changing styles on parent elements now works as expected.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Scott&lt;/strong&gt; for reporting this issue!&lt;/aside&gt;&lt;p&gt;When you&amp;#x27;re editing the HTML of an element you no longer have to click &amp;#x27;Apply changes&amp;#x27;, you can also press &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ Enter&lt;/kbd&gt; so you don&amp;#x27;t have to leave your keyboard.&lt;/p&gt;&lt;p&gt;There were some sites where a specific border-radius style would cause an issue with our box model visualisation (which also shows border radius, shadows and positioning). This has been fixed in Polypane 28 so the box model now always renders correctly.&lt;/p&gt;&lt;img src=&quot;static/boxmodel28-5b539864146be4f01d42f8acd22db8ca.png&quot; class=&quot;imgshadow&quot; alt=&quot;Box model with border radius&quot;/&gt;&lt;p&gt;The attributes tab will now check the &lt;code class=&quot;language-text&quot;&gt;autocomplete&lt;/code&gt; values on input elements and show a warning when an invalid value is used. This makes it easier to spot typos or incorrect values that prevent browsers from autofilling forms correctly.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/elements-panel/&quot;&gt;Learn more about the Elements panel →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;ui-updates&quot;&gt;UI updates&lt;/h2&gt;&lt;p&gt;Polypane 28 includes several UI updates to improve usability and consistency throughout the app.&lt;/p&gt;&lt;h3 id=&quot;new-macos-installer-background&quot;&gt;New macOS installer background&lt;/h3&gt;&lt;p&gt;Our macOS installer now features a new background image that aligns with the updated Polypane branding, and we&amp;#x27;ve also made the icons larger.&lt;/p&gt;&lt;img src=&quot;static/installer-0d29f4ed12e7cd285497fef4ecd20e5b.png&quot; alt=&quot;New macOS installer&quot;/&gt;&lt;h3 id=&quot;panel-design-updates&quot;&gt;Panel design updates&lt;/h3&gt;&lt;p&gt;Several panels have received design updates to improve their usability and visual consistency. The outline panel has a completely new design for when it&amp;#x27;s bottom-aligned to make much better use of the space.&lt;/p&gt;&lt;p&gt;The meta panel now keeps the social media preview header visible as you scroll, so you can quickly switch between light and dark previews, overwrite with different domains or reorder and hide specific previews.&lt;/p&gt;&lt;h3 id=&quot;live-reload-redesign&quot;&gt;Live reload redesign&lt;/h3&gt;&lt;p&gt;The live reload panel has been in Polypane since 2019, and it&amp;#x27;s a beloved feature for many users because it makes testing changes so easy without having to build complex setups.&lt;/p&gt;&lt;img src=&quot;static/livereload-5755c9f809c70e4986002b9504a6045f.png&quot; class=&quot;imgshadow&quot; alt=&quot;&quot;/&gt;&lt;p&gt;In Polypane 28, we&amp;#x27;ve redesigned the live reload panel to better fit with the overall Polypane UI. It&amp;#x27;s now more consistent with the other panels in Polypane and provides more context on the available options.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/live-auto-reloading/&quot;&gt;Learn more about Live reload →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;browse-panel&quot;&gt;Browse panel&lt;/h2&gt;&lt;p&gt;The Browse panel has received several improvements to its ergonomics in Polypane 28. The new bookmarks button lets you quickly open your bookmarks in the Browse panel (even if you&amp;#x27;ve hidden the Project selector). The Browse panel now also has better focus handling for keyboard users, supports default browser shortcuts when focused, and has a new Find in page feature.&lt;/p&gt;&lt;h3 id=&quot;bookmarks-in-browse-panel&quot;&gt;Bookmarks in Browse panel&lt;/h3&gt;&lt;p&gt;The Browse panel now has a new drop down showing your bookmarks for the current project. This makes it easy to open your bookmarked pages directly in the Browse panel.&lt;/p&gt;&lt;img src=&quot;static/bookmarks-48848cc335d154a02afd5299fd0d943c.png&quot; alt=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;p&gt;Additionally, if you&amp;#x27;ve decided to hide the Project Selector in the tab bar, you can still access your bookmarks from the Browse panel.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Matija&lt;/strong&gt; for suggesting this improvement!&lt;/aside&gt;&lt;h3 id=&quot;shortcuts&quot;&gt;Shortcuts&lt;/h3&gt;&lt;p&gt;When the Browse panel has focus, &amp;#x27;normal&amp;#x27; browser shortcuts, like opening and closing tabs or focusing the address bar, work as expected in the Browse panel instead of triggering Polypane-wide shortcuts. This makes it easier to use the Browse panel as a full browser experience without leaving your keyboard.&lt;/p&gt;&lt;h3 id=&quot;search-in-page&quot;&gt;Search in page&lt;/h3&gt;&lt;p&gt;With the Browse panels primary goal of being a supplemental browser where you can have your docs and references open, having a search feature has been a frequently requested feature. In Polypane 28, you can search in the Browse panel using &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ F&lt;/kbd&gt; when it&amp;#x27;s focused to quickly find text on the page.&lt;/p&gt;&lt;img src=&quot;static/findinpage-553734e48467a149064c7e03c1aa871a.png&quot; alt=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;/&gt;&lt;p&gt;Both text and HTML search are supported, and the search results are highlighted on the page as you type.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/browse/&quot;&gt;Learn more about the Browse panel →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;meta-panel&quot;&gt;Meta panel&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve added several nice improvements to the meta panel along with the design update mentioned earlier.&lt;/p&gt;&lt;h3 id=&quot;new-bluesky-preview-for-pages-without-og-image&quot;&gt;New Bluesky preview for pages without OG image&lt;/h3&gt;&lt;p&gt;When a website doesn&amp;#x27;t have an OG image defined, Bluesky uses either the description or the page content to create a rich preview. This is quite different from other social media sites, and now Polypane replicates this behavior in the Meta panel. That way, you see exactly what your users see.&lt;/p&gt;&lt;img src=&quot;static/bskytxt-812db90352df0ff58bf3996e6cee2d4f.png&quot; class=&quot;imgshadow&quot; alt=&quot;Bluesky text preview&quot;/&gt;&lt;h3 id=&quot;securitytxt-warning&quot;&gt;Security.txt warning&lt;/h3&gt;&lt;p&gt;A while back, the security.txt of our website expired and we failed to notice it until someone reached out to us about it. Polypane 28 now checks the expires date of your security.txt and warns you if it&amp;#x27;s expired, or if it&amp;#x27;s about to expire in the next 30 days.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/meta-information/&quot;&gt;Learn more about the Meta panel →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;outline-panel&quot;&gt;Outline panel&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve made various updates to the form overview in the outline panel to make testing forms easier.&lt;/p&gt;&lt;h3 id=&quot;autocomplete-value&quot;&gt;Autocomplete value&lt;/h3&gt;&lt;p&gt;The autocomplete value is now always visible directly below the label, so you can quickly assess if the correct value is being used.&lt;/p&gt;&lt;p&gt;Autocomplete values are automatically checked against a list of valid values for a particular input type and that list is now also aware of the &lt;code class=&quot;language-text&quot;&gt;shipping&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;billing&lt;/code&gt; prefixes for address related values.
When an invalid value is used, a warning icon shows next to the value so you can see which fields need your attention.&lt;/p&gt;&lt;img src=&quot;static/autocompletes-ee649bba906af4dd9f0dd316ced50198.png&quot; class=&quot;imgshadow&quot; alt=&quot;Autocomplete warnings&quot;/&gt;&lt;p&gt;The form overlay now also shows these warnings, making it easier to spot issues when inspecting forms.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Jules&lt;/strong&gt; for suggesting this improvement!&lt;/aside&gt;&lt;h3 id=&quot;form-outline-now-warns-about-small-font-sizes&quot;&gt;Form outline now warns about small font sizes&lt;/h3&gt;&lt;p&gt;The form outline now checks the font size of text input elements and warns you when the font size is smaller than 16px, which causes the page to zoom in on iOS devices on input focus. This is a poor user experience on mobile devices, so the warning helps you catch and fix it early.&lt;/p&gt;&lt;h4 id=&quot;form-outline-expand-all-details-option&quot;&gt;Form outline expand all details option&lt;/h4&gt;&lt;p&gt;You can now click the &amp;#x27;details&amp;#x27; header in the form outline to expand or collapse all details sections at once. This makes it easier to get an overview of all issues or warnings in your forms without having to open each section individually.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/outline-panel/&quot;&gt;Learn more about the Outline panel →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;snippets-from-the-command-bar&quot;&gt;Snippets from the command bar&lt;/h2&gt;&lt;p&gt;You can now activate and deactivate snippets directly from the command bar. Just open the command bar with &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ K&lt;/kbd&gt; and type the name of your snippet to quickly toggle it on or off, or activate it.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Eric&lt;/strong&gt; for suggesting this improvement!&lt;/aside&gt;&lt;img src=&quot;static/snippetscommand-3eb6fbbef0c72daf73aec19db35f11c9.png&quot; class=&quot;imgshadow&quot; alt=&quot;Snippets command bar&quot;/&gt;&lt;p&gt;This lets you quickly apply (and remove) snippets without having to open the snippets manager, and it&amp;#x27;s all accessible from your keyboard.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;/docs/snippets/&quot;&gt;Learn more about snippets →&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;page-load-performance&quot;&gt;Page Load performance&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve made several improvements specifically to the page load performance in Polypane 28. Polypane now does less work setting up its various listeners and indexers when loading a page, resulting in faster load times, especially on pages with a lot of elements or complex structures.&lt;/p&gt;&lt;h2 id=&quot;polypane-and-wcag-updates&quot;&gt;Polypane and WCAG updates&lt;/h2&gt;&lt;p&gt;Accessibility consultant &lt;a href=&quot;https://www.200ok.nl/&quot;&gt;Jules Ernst&lt;/a&gt; has updated his overview of WCAG success criteria and how Polypane helps you test for them.&lt;/p&gt;&lt;p&gt;Check out the updated &lt;a href=&quot;https://www.200ok.nl/tips/polypane-and-wcag/&quot;&gt;Polypane and WCAG overview&lt;/a&gt; on his site for the latest information.&lt;/p&gt;&lt;aside class=&quot;thanks-callout&quot; style=&quot;background:rgb(0, 96, 129) linear-gradient(135deg, rgb(0, 96, 129), #47924f);color:#fff;text-align:center;display:block;width:fit-content;border-radius:0.5rem;padding:.25rem .5rem;margin:1.5rem auto;font-size:0.95rem;box-shadow:rgba(0, 0, 0, 0.025) 0 0 0 1px,
        rgba(0, 0, 0, 0.05) 0px 1px 0px,
        rgba(0, 0, 0, 0.03) 0px 0px 8px,
        rgba(0, 0, 0, 0.1) 0px 20px 30px&quot;&gt;&lt;strong&gt;💙 Thanks Jules&lt;/strong&gt; for keeping this overview up to date!&lt;/aside&gt;&lt;h2 id=&quot;chromium-146&quot;&gt;Chromium 146&lt;/h2&gt;&lt;p&gt;Polypane 28 is built on Chromium 146. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-28&quot;&gt;Get Polypane 28&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14 day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-2803-changelog&quot;&gt;Polypane 28.0.3 Changelog&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Issue preventing Mac users from auto-updating&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-2802-changelog&quot;&gt;Polypane 28.0.2 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Added cmd/ctrl+shift+d shortcut to reload all debug tools (Thanks Florian!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippet manager UI improvements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: now ignores hidden headings for duplicate content checks&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Improved logic for aside elements in sectioning content (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; More resilient build and update internals&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Improve performance for large DOM trees&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Context menu performance in browse panel&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Peek now shows the currentSrc for videos&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Warn when the og:url is not the same as the canonical or page url&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Breakpoints no longer list negative value widths&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Breakpoint panes adhere to the minimum pane width&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Reduced installer download size by an average of 25%&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated list of Google Fonts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated Chromium to 146.0.7680.31&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Prevent Polypane shortcuts overwriting global ones on app launch (Thanks Benoit!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: issue where autocomplete cleared the typed value when adding attributes or css values&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Color picker not setting correct background color when inspecting from Elements panel&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Snippets: Last save location is now correctly remembered when saving snippets&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Install extensions from suggestion box when you have multiple sessions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Reinstate sync engine after main window reopen on macOS&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-2801-changelog&quot;&gt;Polypane 28.0.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Environments switcher: Add cmd + click as option to cycle through environments for mac (Thanks Nicolas!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Improved Bluesky preview for pages with missing og info (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Form list now also indexes role=form&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Address bar now recognises localhost paths as valid urls (Thanks Harald!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Portal: You can now middle-click the icon to close the portal&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Peek: Improved area rendering logic&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: prevent collecting styles too fast for CSS-in-JS libraries to apply (Thanks Lara!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Show force starting-style option for styles nested inside starting style&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Calculation error in box model (Thanks Bramus!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Currently selected element could not be collapsed by mouseclick (Thanks Harald!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: Breadcrumbslist microdata with single item would break updates (Thanks Marc!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Environments dropdown: Prevent horizontal scroll in dropdown&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-28-changelog&quot;&gt;Polypane 28 Changelog&lt;/h2&gt;&lt;p&gt;Full changelog (yes, there&amp;#x27;s more!):&lt;/p&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Environments for Projects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Edit CSS Selectors&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Console: Support for console.group (Thanks Giles!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Command bar: Activate or deactivate snippets from the command bar (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Chromium 146.0.7650.0&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Projects, Bookmarks and Environments can now be re-ordered (Thanks Michael!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Projects: Open or switch from the CLI (Thanks Marc!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Projects: Open or switch from the macOS and windows dock/taskbar (Thanks Benoit!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Constructed stylesheets no longer open browse tabs when clicked&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Updated performance when inspecting elements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: make !important styles more visible&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: press &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘ Enter&lt;/kbd&gt; when editing HTML to apply it&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Show warning for invalid autocomplete values&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Support for &lt;code class=&quot;language-text&quot;&gt;::search-text&lt;/code&gt; pseudo-element styling&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Improved UI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Handle broken link tags on pages&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Link meta tags containing URLS are now clickable&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Show the type of &amp;#x27;alternate&amp;#x27; link tags&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: New Bluesky preview for pages without OG image&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Show warning if security.txt is expired&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Allow webp for og images&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Updated Discord designs&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Form outline warns about small font sizes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: New bottom-aligned panel layout&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Support shipping and billing values in Form autocomplete&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Expand all details option (Thanks Jules!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: show autocomplete warnings in form overlay&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Context menu: Hold &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘&lt;/kbd&gt; when clicking &amp;#x27;reload pane&amp;#x27; to do a hard reload&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Cache: Disable cache now applies to panes instead of sessions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Improved page load performance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Improved tab creation performance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug tools: Polypane UI is excluded from placeholdifier debug tool (Thanks Dave!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse/devtools panel: Improve resize performance&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Show bookmarks in Browse panel (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Live reload: Redesigned to fit better with Polypane UI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Consistent &amp;#x27;clear input&amp;#x27; UI throughout the app&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; New macOS installer background&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Tab customisation UI redesign&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Panes without navigation sync now get a color indicator when environments or domains match&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Search in page (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Better focus handling for keyboard users (Thanks Matija!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Support for default browser shortcuts when focused&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Rulers: Middle click button to disable rulers quickly&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Reference image: updated UI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; When setting a new default new pane, the default new layout is updated too (Thanks Michael!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Form autofilling now has Dutch and Canadian postal code formats (Thanks Maarten!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; We no longer hide the browse and devtools panels when drop downs overlay them&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Emoji pickers now always start with search, and default to Unicode 16 emojis&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessibility panel: Update ruleset&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated list of Google fonts&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Prevent UI from breaking when there are many open tabs (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Projects: prevent duplicate project names&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Browse panel: Prevent issue where new tabs could not be created&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Snippet panel: Prevent stale focus outlines after use&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Box model would sometimes not render (Thanks Scott!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Updating parent styles works again (Thanks Scott!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Make sure numbering and focus arrows are visible again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Prevent link errors from being clipped&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Show broken links now filters list again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Tracking button in detached panel works again&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Detached panel did not always get new data&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: add missing &lt;code class=&quot;language-text&quot;&gt;street-address&lt;/code&gt; autocomplete value&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: correctly support autocomplete on implicitly typed input elements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Storage panel: Fix bug where session could not be accessed for initial panes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: Robots.txt duplicate warning layout issue&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Bookmarks: Middle clicks open new tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Context menus in Chrome devtools panel not showing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Emulation: Correctly show the current network throttling profile&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Emulation: scrolled settings no longer prevent tabs from being clickable (Thanks Dave!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Update measured screenshot button correctly on activation&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Background color of overview screenshots did not match UI&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Request new permission on launch to allow sites to use desktopCapture on macOS&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Understanding the fundamentals of CSS Layout]]></title><description><![CDATA[When developers say that CSS is hard, they're usually talking about CSS layout. What often gets omitted though is that developers are…]]></description><link>https://polypane.app/blog/understanding-the-fundamentals-of-css-layout/</link><guid isPermaLink="false">https://polypane.app/blog/understanding-the-fundamentals-of-css-layout/</guid><pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When developers say that CSS is hard, they&amp;#x27;re usually talking about CSS layout. What often gets omitted though is that developers are assumed to understand and effectively use CSS without being taught how it works in the first place.&lt;/p&gt;&lt;p&gt;I think this is because the syntax of CSS is simple, especially compared to JavaScript and even to HTML.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;select-thing-here&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;guess-a-property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; give-it-a-value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* ...and repeat */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It leads to the impression that all you need to write CSS is to understand the syntax and have a reference for the properties and values.&lt;/p&gt;&lt;p&gt;That, of course, is not true.&lt;/p&gt;&lt;p&gt;Imagine if you were assumed to learn JS by just looking at its syntax. How would you ever learn what &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; refers to, or how closures work, or how prototypal inheritance works? It would make no sense!&lt;/p&gt;&lt;p&gt;CSS is no less deserving of understanding its underlying concepts, so let&amp;#x27;s build that foundation.&lt;/p&gt;&lt;h2 id=&quot;prefer-video&quot;&gt;Prefer video?&lt;/h2&gt;&lt;p&gt;This article is a written version of a talk by Kilian Valkhof, Polypane&amp;#x27;s creator. You can watch the video here:&lt;/p&gt;&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom:56.25%;position:relative;height:0;overflow:hidden&quot;&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/Al4yGtRYyew&quot; frameBorder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot;position:absolute;top:0;left:0;width:100%;height:100%&quot;&gt;&lt;/iframe&gt; &lt;/div&gt;&lt;p&gt;&lt;em&gt;Interested in having this talk at your conference or meetup? &lt;a href=&quot;https://kilianvalkhof.com/speaking/&quot;&gt;Get in touch!&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;&lt;h2 id=&quot;galls-law&quot;&gt;Gall&amp;#x27;s law&lt;/h2&gt;&lt;p&gt;When you look at CSS as a whole, it&amp;#x27;s a complex system that is hard to understand. But it&amp;#x27;s also a system that works.&lt;/p&gt;&lt;p&gt;We have a programming principle that covers that:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;All complex systems that work, evolved from simple systems that worked.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This is known as Gall&amp;#x27;s law, and it applies to CSS layout as well. At its core, CSS layout is built on a few fundamental concepts that work together to create the complex layouts we see on the web today.&lt;/p&gt;&lt;p&gt;CSS Layout started out with a single layout algorithm called &lt;strong&gt;normal flow&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Normal flow has a few basic rules, which all subsequent layout algorithms built upon. Indeed, that&amp;#x27;s the first lesson:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;CSS has &lt;em&gt;multiple&lt;/em&gt; layout algorithms.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Specific properties in CSS let you opt into different layout algorithms, each with their own rules and behaviors.&lt;/p&gt;&lt;p&gt;Once you understand the foundation of normal flow, you can start to understand how other layout algorithms like positioning, Flexbox, and Grid build upon it.&lt;/p&gt;&lt;p&gt;In other words, this article will help you go from this:&lt;/p&gt;&lt;img src=&quot;/blogs/csslayout/cssbad.gif&quot; alt=&quot;&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;…to this:&lt;/p&gt;&lt;img src=&quot;/blogs/csslayout/cssgood.gif&quot; alt=&quot;&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Are you ready?&lt;/p&gt;&lt;div style=&quot;border:5px solid dodgerblue;width:200px;height:200px;margin:2rem auto&quot;&gt;&lt;/div&gt;&lt;h2 id=&quot;everything-is-a-box&quot;&gt;Everything is a box&lt;/h2&gt;&lt;p&gt;The most fundamental concept in CSS is that &lt;strong&gt;everything is a box&lt;/strong&gt;. Every element creates a rectangular box, and the x and y position of the box, along with it&amp;#x27;s width and height create the combined layout on the page.&lt;/p&gt;&lt;p&gt;CSS is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A bunch of boxes,&lt;/li&gt;&lt;li&gt;placed on the screen,&lt;/li&gt;&lt;li&gt;according to layout algorithms.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This might seem like an obvious statement, but it&amp;#x27;s important to understand: before screens became ubiquitous, the idea of a &amp;quot;live&amp;quot; layout algorithm wasn&amp;#x27;t really needed. Boxes didn&amp;#x27;t interact with each other. They were put in a place and then just ...stayed there.&lt;/p&gt;&lt;p&gt;Print pages are &lt;em&gt;laid out&lt;/em&gt; of course, and have been for centuries, but that didn&amp;#x27;t really happen in real time by a system of rules, an algorithm.&lt;/p&gt;&lt;p&gt;So, CSS is a bunch of boxes. The initial layout algorithm that places them on the screen is:&lt;/p&gt;&lt;h2 id=&quot;normal-flow&quot;&gt;Normal flow&lt;/h2&gt;&lt;p&gt;Normal flow is the default layout algorithm in CSS. It&amp;#x27;s what you get when you don&amp;#x27;t explicitly opt into any other layout algorithm.&lt;/p&gt;&lt;p&gt;It comes from when the web was made for document sharing, and &amp;quot;normal flow&amp;quot; refers to the way text flows across a page: &lt;strong&gt;from left to right, from top to bottom&lt;/strong&gt; (for western languages).&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Normal flow is the default - no CSS needed! */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From left to right is the &lt;em&gt;inline&lt;/em&gt; direction, from top to bottom is the &lt;em&gt;block&lt;/em&gt; direction. So text
characters will be inline, side by side, and wrap to the next line. Paragraphs and headings are block, and they stack vertically.&lt;/p&gt;&lt;div class=&quot;box&quot; style=&quot;margin:1rem auto;position:relative;border:5px solid dodgerblue;width:200px;height:180px;padding-left:0.5rem&quot;&gt;&lt;p style=&quot;font-size:2rem&quot;&gt;Inline →&lt;/p&gt;&lt;p style=&quot;font-size:2rem;transform-origin:0 0;transform:rotate(90deg);position:absolute;left:-5%;top:0;white-space:nowrap&quot;&gt;Block →&lt;/p&gt;&lt;/div&gt;&lt;p&gt;Browsers ship with a default stylesheet that dictates which elements are block and which are inline: &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; is block, but &lt;code class=&quot;language-text&quot;&gt;&amp;lt;em&amp;gt;&lt;/code&gt; is inline.&lt;/p&gt;&lt;p&gt;So that&amp;#x27;s why, if you write semantic HTML, the browser already makes your page look like a document.&lt;/p&gt;&lt;h3 id=&quot;non-western-languages&quot;&gt;Non-western languages&lt;/h3&gt;&lt;p&gt;Not all languages are written left to right, top to bottom. Some languages are written right to left (like Arabic and Hebrew), and some are written top to bottom (like traditional Chinese and Japanese).&lt;/p&gt;&lt;p&gt;This is why CSS has a &lt;code class=&quot;language-text&quot;&gt;writing-mode&lt;/code&gt; property that lets you change the inline and block directions to fit the language you&amp;#x27;re using.&lt;/p&gt;&lt;p&gt;Along with that writing mode, many of the explicit directional and sizing properties, like &amp;quot;left&amp;quot; or &amp;quot;height&amp;quot; now have logical equivalents. Instead of &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;, which for western languages is the start of inline text, the property name is &lt;code class=&quot;language-text&quot;&gt;inset-inline-start&lt;/code&gt;. Instead of &lt;code class=&quot;language-text&quot;&gt;height&lt;/code&gt;, which is the size in the block direction, the property name is &lt;code class=&quot;language-text&quot;&gt;block-size&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Writing CSS with these logical properties makes them more portable. For this article though, we&amp;#x27;ll stick to the physical properties like &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; for simplicity.&lt;/p&gt;&lt;h3 id=&quot;formatting-context-and-anonymous-boxes&quot;&gt;Formatting context and anonymous boxes&lt;/h3&gt;&lt;p&gt;Block and inline tell you how a given element lays out in comparison to the elements around it. This state of being block or inline is called the &lt;strong&gt;formatting context&lt;/strong&gt;, and each element has a formatting context.&lt;/p&gt;&lt;p&gt;But not everything is an element. Consider this HTML:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;This is &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;emphasized&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;em&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; is a block element and &lt;code class=&quot;language-text&quot;&gt;&amp;lt;em&amp;gt;&lt;/code&gt; is inline.&lt;/p&gt;&lt;p&gt;But what about the words &amp;quot;This is&amp;quot;? How does that work?&lt;/p&gt;&lt;p&gt;Earlier I wrote that in CSS, everything is a box, and that&amp;#x27;s also how normal flow solves this. The browser creates something called an &lt;strong&gt;anonymous box&lt;/strong&gt; around that text. In this case because &lt;code class=&quot;language-text&quot;&gt;&amp;lt;em&amp;gt;&lt;/code&gt; is an inline box, the anonymous box will also be inline: it will be an &lt;em&gt;anonymous inline box&lt;/em&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&amp;lt;p&amp;gt;&lt;span style=&quot;border:2px solid hotpink&quot;&gt;This is &lt;/span&gt;&amp;lt;em&amp;gt;emphasized&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With that, the browsers now has two inline boxes, and it knows what do to with that. It can lay them out next to each other.&lt;/p&gt;&lt;p&gt;If the code was this though:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  This is
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;a new paragraph&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; are block-level, so the anonymous box generated for &amp;quot;This is&amp;quot; will be an &lt;em&gt;anonymous &lt;strong&gt;block&lt;/strong&gt; box&lt;/em&gt;, and the browser can lay out the two boxes on top of each other:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&amp;lt;div&amp;gt;&lt;br/&gt;&lt;div style=&quot;border:2px solid hotpink&quot;&gt;  This is&lt;/div&gt;&lt;div&gt;  &amp;lt;p&amp;gt;a new paragraph&amp;lt;/p&amp;gt;&lt;/div&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;whitespace-and-anonymous-boxes&quot;&gt;Whitespace and anonymous boxes&lt;/h3&gt;&lt;p&gt;This anonymous box generation is why some things you might expect to work don&amp;#x27;t. Take this example.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--examplecontainer--xkqcM&quot;&gt;&lt;div class=&quot;examples-styles-module--code--Apw_d&quot;&gt;&lt;div class=&quot;examples-styles-module--block--1nGFC&quot; style=&quot;height:140px&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; style=&quot;translate:-0px -0px&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;textarea spellcheck=&quot;false&quot;&gt;&amp;lt;div&amp;gt;
  &amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;
  &amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/textarea&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--block--1nGFC&quot; style=&quot;height:250px&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; style=&quot;translate:-0px -0px&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;.half {
    display: inline-block;
    width: 50%;

    background: hotpink;
}
.half + .half {
    background: dodgerblue;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;textarea spellcheck=&quot;false&quot;&gt;.half {
    display: inline-block;
    width: 50%;

    background: hotpink;
}
.half + .half {
    background: dodgerblue;
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--result--2NMZR&quot;&gt;&lt;iframe title=&quot;result&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Both divs are 50% wide, but they don&amp;#x27;t sit on the same line. Even though 50% + 50% is 100%.&lt;/p&gt;&lt;p&gt;This is caused by an anonymous inline box created by the newline, the whitespace, between the two divs. That inline box takes up space (the width of a single space character) to the right of the first div, and so the second div no longer fits on the same line:&lt;/p&gt;&lt;div class=&quot;anomymous-example&quot;&gt;&lt;div class=&quot;half&quot; style=&quot;background:hotpink&quot;&gt;First&lt;/div&gt;&lt;div class=&quot;anonymousbox&quot;&gt; &lt;/div&gt;&lt;div class=&quot;half&quot; style=&quot;background:dodgerblue&quot;&gt;Second&lt;/div&gt;&lt;/div&gt;&lt;p&gt;It would be great if we can select that anonymous box and hide it but unfortunately that&amp;#x27;s not something we can do with CSS.&lt;/p&gt;&lt;p&gt;We can only target named boxes.&lt;/p&gt;&lt;h4 id=&quot;solving-the-whitespace-issue&quot;&gt;Solving the whitespace issue&lt;/h4&gt;&lt;p&gt;We can solve this whitespace problem in several ways:&lt;/p&gt;&lt;h5 id=&quot;remove-the-whitespace&quot;&gt;Remove the whitespace&lt;/h5&gt;&lt;p&gt;Remove the character space between the two &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;s, and the anonymous box is never created (...until you run &lt;code class=&quot;language-text&quot;&gt;prettier&lt;/code&gt; on your code).&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&amp;lt;div&amp;gt;&lt;br/&gt;  &amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;half&amp;quot;&amp;gt;&lt;br/&gt;  &amp;lt;/div&amp;gt;&lt;br/&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;anomymous-example&quot;&gt;&lt;div class=&quot;half&quot; style=&quot;background:hotpink&quot;&gt;First&lt;/div&gt;&lt;div class=&quot;half&quot; style=&quot;background:dodgerblue&quot;&gt;Second&lt;/div&gt;&lt;/div&gt;&lt;h5 id=&quot;disable-wrapping&quot;&gt;Disable wrapping&lt;/h5&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nowrap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;anomymous-example&quot; style=&quot;white-space:nowrap;border:2px solid black&quot;&gt;&lt;div class=&quot;half&quot; style=&quot;background:hotpink&quot;&gt;First&lt;/div&gt;&lt;span&gt; &lt;/span&gt;&lt;div class=&quot;half&quot; style=&quot;background:dodgerblue&quot;&gt;Second&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When you disable wrapping, you force both &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;s on the same line regardless of the width of the container. Notice that there is still that space between the two &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;s, they don&amp;#x27;t sit flush against each other and that the second &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; overflows the container.&lt;/p&gt;&lt;p&gt;Since the total width is still 50% + the width of a space character + 50%, the second &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; overflows the container.&lt;/p&gt;&lt;h5 id=&quot;set-the-font-size-to-0&quot;&gt;Set the font-size to 0&lt;/h5&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container &amp;gt; div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Reset for children */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;anomymous-example&quot; style=&quot;font-size:0&quot;&gt;&lt;div class=&quot;half&quot; style=&quot;background:hotpink;font-size:16px&quot;&gt;First&lt;/div&gt;&lt;div class=&quot;half&quot; style=&quot;background:dodgerblue;font-size:16px&quot;&gt;Second&lt;/div&gt;&lt;/div&gt;&lt;p&gt;If you set the font-size of the parent to 0, the anonymous box will still be created, but it won&amp;#x27;t have any width since the width of a space at font-size 0 is 0.&lt;/p&gt;&lt;p&gt;If your child elements have text in them, you will need to reset the font size in them or your text won&amp;#x27;t show up either.&lt;/p&gt;&lt;h5 id=&quot;white-space-collapse-discard-in-the-future&quot;&gt;White-space-collapse: discard (in the future!)&lt;/h5&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;white-space-collapse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; discard&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;white-space-collapse:discard&quot;&gt;&lt;div class=&quot;half&quot; style=&quot;background:hotpink&quot;&gt;First&lt;/div&gt; &lt;div class=&quot;half&quot; style=&quot;background:dodgerblue&quot;&gt;Second&lt;/div&gt;&lt;/div&gt;&lt;p&gt;To show you how much CSS is still being worked on, white-space-collapse
is part of the newest CSS specification, text level 4.&lt;/p&gt;&lt;p&gt;The value we mention above, &amp;#x27;discard&amp;#x27;, isn&amp;#x27;t even supported in any browser as of January 2026. So this is a feature for the future and doesn&amp;#x27;t work yet.&lt;/p&gt;&lt;p&gt;Even normal flow, which has been around since &lt;em&gt;literally the beginning of CSS&lt;/em&gt;, is
still being worked on!&lt;/p&gt;&lt;h3 id=&quot;the-box-model&quot;&gt;The box model&lt;/h3&gt;&lt;p&gt;We&amp;#x27;re not done talking about boxes interact by a long shot, but for the next few concepts we need to understand how boxes themselves are built up. For that we need to talk about the box model.&lt;/p&gt;&lt;p&gt;You have probably seen a version of this diagram before. A box in CSS actually
consists of a few distinct areas.&lt;/p&gt;&lt;p&gt;The center area is where you content is, the text or child elements. This content is pushed inwards by the padding. The edge of a box is called the border, and outside of the border is the margin which pushes a box away from other boxes.&lt;/p&gt;&lt;div class=&quot;boxmodel&quot;&gt;margin&lt;div&gt;border&lt;div&gt;padding&lt;div&gt;content&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;For stupid browser reasons there are two models when it comes to determining the size of a box when you
give it a width: &lt;strong&gt;content-box&lt;/strong&gt; and &lt;strong&gt;border-box&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;One of those is the default in browsers, and the other one
makes sense.&lt;/p&gt;&lt;h4 id=&quot;content-box&quot;&gt;Content-box&lt;/h4&gt;&lt;p&gt;The default is &lt;code class=&quot;language-text&quot;&gt;content-box&lt;/code&gt;. In that model, &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; is applied to the &lt;code class=&quot;language-text&quot;&gt;content-box&lt;/code&gt;, and padding and borders are added to it for the final rendered width.&lt;/p&gt;&lt;div class=&quot;boxmodel&quot;&gt;margin&lt;div&gt;border&lt;div&gt;padding&lt;div style=&quot;border:2px solid red&quot;&gt;content&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Actual width = width + padding + border&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is confusing because when you set &lt;code class=&quot;language-text&quot;&gt;width: 200px&lt;/code&gt;, the actual rendered width might be 250px after padding and borders.&lt;/p&gt;&lt;h4 id=&quot;border-box&quot;&gt;Border-box&lt;/h4&gt;&lt;p&gt;To improve this situation, most modern CSS resets include the following CSS&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With &lt;code class=&quot;language-text&quot;&gt;border-box&lt;/code&gt;, the width you set is applied to the &lt;code class=&quot;language-text&quot;&gt;border-box&lt;/code&gt; of the element, so it &lt;strong&gt;includes&lt;/strong&gt; the border and padding layers. The space for the content is width minus padding minus border.&lt;/p&gt;&lt;div class=&quot;boxmodel&quot;&gt;margin&lt;div style=&quot;border:2px solid red&quot;&gt;border&lt;div&gt;padding&lt;div&gt;content&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Actual width = width&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This means that when you see &lt;code class=&quot;language-text&quot;&gt;width: 200px&lt;/code&gt; in your CSS, you know the element is actually 200px wide regardless of the padding and border. It&amp;#x27;s much easier to work with.&lt;/p&gt;&lt;p&gt;Try editing the values in the example below to see how &lt;code class=&quot;language-text&quot;&gt;content-box&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;border-box&lt;/code&gt; differ. Notice that both boxes have the same width value (200px), but the content-box is physically larger because padding and borders are added outside the content width.&lt;/p&gt;&lt;div class=&quot;examples-styles-module--examplecontainer--xkqcM&quot;&gt;&lt;div class=&quot;examples-styles-module--code--Apw_d&quot;&gt;&lt;div class=&quot;examples-styles-module--block--1nGFC&quot; style=&quot;height:100px&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; style=&quot;translate:-0px -0px&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;content-box&amp;quot;&amp;gt;Content Box&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;border-box&amp;quot;&amp;gt;Border Box&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;textarea spellcheck=&quot;false&quot;&gt;&amp;lt;div class=&amp;quot;content-box&amp;quot;&amp;gt;Content Box&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;border-box&amp;quot;&amp;gt;Border Box&amp;lt;/div&amp;gt;&lt;/textarea&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--block--1nGFC&quot; style=&quot;height:440px&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; style=&quot;translate:-0px -0px&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code&gt;div {
  width: 200px;
  padding: 20px;
  margin-bottom: 1rem;
}
.content-box {
  box-sizing: content-box;
  border: 5px solid blue;
  background: lightblue;
}
.border-box {
  box-sizing: border-box;
  border: 5px solid green;
  background: lightgreen;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;textarea spellcheck=&quot;false&quot;&gt;div {
  width: 200px;
  padding: 20px;
  margin-bottom: 1rem;
}
.content-box {
  box-sizing: content-box;
  border: 5px solid blue;
  background: lightblue;
}
.border-box {
  box-sizing: border-box;
  border: 5px solid green;
  background: lightgreen;
}&lt;/textarea&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;examples-styles-module--result--2NMZR&quot;&gt;&lt;iframe title=&quot;result&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;With this understanding of the box model, we can go back to talking about how boxes interact.&lt;/p&gt;&lt;h3 id=&quot;how-inline-boxes-work&quot;&gt;How inline boxes work&lt;/h3&gt;&lt;p&gt;Everything in CSS is a box, but &amp;quot;box&amp;quot; isn&amp;#x27;t always what you expect.&lt;/p&gt;&lt;p&gt;A block box makes sense: the element is a rectangle, and the border sits on all edges.&lt;/p&gt;&lt;div class=&quot;box&quot; aria-hidden=&quot;true&quot; style=&quot;margin:1rem auto;position:relative;border:5px solid dodgerblue;display:block;width:50%;color:transparent;line-height:4;font-size:20px&quot;&gt;gr gerg dg dsgf d gzrg gr drg xesrg es&lt;/div&gt;&lt;p&gt;But inline elements can wrap to the next line without becoming a block element, and so the box around an inline element is split up and looks, well, kind of broken:&lt;/p&gt;&lt;div class=&quot;fragment&quot; style=&quot;text-align:center;width:50%;margin:1rem auto;line-height:4&quot;&gt;&lt;span class=&quot;box&quot; style=&quot;color:transparent;padding:10px;border:5px solid dodgerblue;text-align:center&quot; aria-hidden=&quot;true&quot;&gt;gr gerg dg dsgf d gzrg gr drg xesrg es gr gerg dg dsgf d gzrg gr drg xesrg es gr gerg dg dsgf d gzrg gr drg xesrg es r gerg dg dsgf d gzrg gr drg xesrg es&lt;/span&gt;&lt;/div&gt;&lt;p&gt;The top and bottom borders are drawn for each line the element spans, while the inline borders and padding (left and right) are only drawn at the very start and very end.&lt;/p&gt;&lt;p&gt;That looks really broken, doesn&amp;#x27;t it? Browsers render an inline element as one, long, single line (it&amp;#x27;s inline after all), and then they slice that up into different line segments where it wraps. Thinking about it in that way makes it a bit easier to visualize what is happening and why it&amp;#x27;s happening. The box is sliced up into different parts.&lt;/p&gt;&lt;h4 id=&quot;decorations&quot;&gt;Decorations&lt;/h4&gt;&lt;p&gt;If you have &amp;quot;decorations&amp;quot; (which is what borders and padding are called) on an inline element, you probably expect those to be applied to each line segment.&lt;/p&gt;&lt;p&gt;Luckily, we can tell browsers how to treat each broken-up line with &lt;code class=&quot;language-text&quot;&gt;box-decoration-break&lt;/code&gt;. The default value is &lt;code class=&quot;language-text&quot;&gt;slice&lt;/code&gt;, which is quite literally slicing up the box into different parts.&lt;/p&gt;&lt;p&gt;But you can also tell the browser to treat each line as its own box with &lt;code class=&quot;language-text&quot;&gt;clone&lt;/code&gt;. Each line becomes its own box with borders and padding on all sides.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-decoration-break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; clone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;fragment&quot; style=&quot;text-align:center;width:50%;margin:1rem auto;line-height:4&quot;&gt;&lt;span class=&quot;box&quot; style=&quot;color:transparent;padding:10px;border:5px solid dodgerblue;box-decoration-break:clone;text-align:center&quot; aria-hidden=&quot;true&quot;&gt;gr gerg dg dsgf d gzrg gr drg xesrg es gr gerg dg dsgf d gzrg gr drg xesrg es gr gerg dg dsgf d gzrg gr drg xesrg es r gerg dg dsgf d gzrg gr drg xesrg es&lt;/span&gt;&lt;/div&gt;&lt;h4 id=&quot;placement-of-inline-elements&quot;&gt;Placement of inline elements&lt;/h4&gt;&lt;p&gt;Some things like vertical margins, explicit widths and height don&amp;#x27;t apply to inline elements. This is because when an element is &amp;quot;in line&amp;quot;, it&amp;#x27;s placement on the page is determined by the line it&amp;#x27;s in. So we know text is &amp;quot;in line&amp;quot;, but what&amp;#x27;s a line?&lt;/p&gt;&lt;h3 id=&quot;lines-and-baselines&quot;&gt;Lines and baselines&lt;/h3&gt;&lt;p&gt;Browsers started out as a way to lay out lines of text. The inline text inside a box is laid out in a number of &lt;em&gt;lines&lt;/em&gt;, and these lines are laid out based on the &lt;code class=&quot;language-text&quot;&gt;line-height&lt;/code&gt;:&lt;/p&gt;&lt;div style=&quot;border:5px solid dodgerblue;margin:2rem 0;text-align:left;position:relative;font-size:5rem;font-weight:bold;line-height:1.5;overflow:hidden&quot;&gt;What are lines in browsers?&lt;div class=&quot;fragment&quot; style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:50%&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Within each of these lines there is a &lt;strong&gt;baseline&lt;/strong&gt;. The baseline is determined by the font you use, and it&amp;#x27;s the bottom of your text characters (except for descenders, which go below the baseline).&lt;/p&gt;&lt;div style=&quot;border:5px solid dodgerblue;margin:2rem 0;text-align:left;position:relative;font-size:5rem;font-weight:bold;line-height:1.5;overflow:hidden&quot;&gt;What are lines in browsers?&lt;div style=&quot;background:grey;height:1px;width:100%;position:absolute;right:0;left:0;top:50%;opacity:0.5&quot;&gt;&lt;/div&gt;&lt;div class=&quot;fragment&quot;&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:37%&quot;&gt;&lt;/div&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:87%&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;What you should understand here is that:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Inline elements are laid out in lines&lt;/li&gt;&lt;li&gt;Each line has a baseline&lt;/li&gt;&lt;li&gt;The baseline is determined by the font&lt;/li&gt;&lt;li&gt;All inline elements align with the baseline &lt;em&gt;by default&lt;/em&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;IF you want your text to be in a different place (vertically) in the line, you can change the vertical alignment of an inline element with &lt;code class=&quot;language-text&quot;&gt;vertical-align&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; baseline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* default */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; middle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bottom&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text-top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text-bottom&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;bottom&lt;/code&gt; values are determined by the line height you set on the element:&lt;/p&gt;&lt;div style=&quot;overflow:auto;display:block;line-height:2;border:5px solid dodgerblue;margin:2rem;text-align:center;position:relative;font-size:4rem;font-weight:bold&quot;&gt;&lt;span style=&quot;vertical-align:top;font-size:0.8em;line-height:1&quot;&gt;top&lt;/span&gt;Baseline&lt;span style=&quot;vertical-align:bottom;font-size:0.8em;line-height:1&quot;&gt;bottom&lt;/span&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:68%&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;text-top&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;text-bottom&lt;/code&gt; are determined by the font. Text-top will align the x-height with the top of ascenders (the parts of letters like b, d, f, h that extend above the x-height), and text-bottom will align the bottom of normal letters with the bottom of descenders.&lt;/p&gt;&lt;div style=&quot;display:block;line-height:2;border:5px solid dodgerblue;margin:2rem;text-align:center;position:relative;font-size:4rem;font-weight:bold;overflow:hidden&quot;&gt;&lt;span style=&quot;vertical-align:text-top;font-size:0.8em;line-height:1&quot;&gt;text-top&lt;/span&gt;Baseline&lt;span style=&quot;vertical-align:text-bottom;font-size:0.8em;line-height:1&quot;&gt;text-bottom&lt;/span&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:68%&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Lastly, &lt;code class=&quot;language-text&quot;&gt;middle&lt;/code&gt; is weird.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s calculated from a combination of the font metrics and the line-height, and that causes it to not always mean the exact middle of the line:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;middle = baseline + (x-height / 2)&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In the example below, you can see that the word &amp;quot;middle&amp;quot; is actually closer to the bottom of the line than to the top.&lt;/p&gt;&lt;div style=&quot;display:block;line-height:2;border:5px solid dodgerblue;margin:2rem;text-align:center;position:relative;font-size:5rem;font-weight:bold;overflow:hidden&quot;&gt;Baseline &lt;span style=&quot;vertical-align:middle&quot;&gt;middle&lt;/span&gt;&lt;div style=&quot;background:grey;height:1px;width:100%;position:absolute;right:0;left:0;top:66%&quot;&gt;&lt;/div&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:33%&quot;&gt;&lt;/div&gt;&lt;div style=&quot;background:dodgerblue;height:1px;width:100%;position:absolute;right:0;left:0;top:70%&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;I&amp;#x27;d like to apologize for that on behalf of CSS.&lt;/p&gt;&lt;p&gt;For practical purposes, just know it exists but might not behave exactly as you expect.&lt;/p&gt;&lt;p&gt;From the explanation above, what you need to remember is that, by default, &lt;em&gt;all&lt;/em&gt; inline elements sit on the baseline.&lt;/p&gt;&lt;h4 id=&quot;inline-elements-sit-on-the-baseline&quot;&gt;Inline elements sit on the baseline&lt;/h4&gt;&lt;p&gt;With that knowledge, does the following situation already make sense?&lt;/p&gt;&lt;div style=&quot;display:block;border:5px solid dodgerblue;position:relative;margin:2rem 0;text-align:center&quot;&gt;&lt;img src=&quot;/blogs/csslayout/kitten.jpg&quot; style=&quot;width:200px;padding:0;margin:0;display:inline&quot;/&gt;&lt;/div&gt;&lt;p&gt;If you now find this obvious, then good job! The inline image is sitting on the baseline, and that&amp;#x27;s why there is &amp;#x27;unexpected&amp;#x27; whitespace below it.&lt;/p&gt;&lt;p&gt;In the past, you might have fixed that by setting &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt; on the image, but this has other layout effects too:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:block;border:5px solid dodgerblue;position:relative;margin:2rem 0;text-align:center&quot;&gt;&lt;img src=&quot;/blogs/csslayout/kitten.jpg&quot; style=&quot;width:200px;padding:0;margin:0;display:block&quot;/&gt;&lt;/div&gt;&lt;p&gt;The whitespace is gone, but the image now also no longer sits inline with any text, nor does it follow the text align.&lt;/p&gt;&lt;p&gt;Another way you could have fixed this is by setting &lt;code class=&quot;language-text&quot;&gt;line-height: 0&lt;/code&gt; on the parent:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:block;border:5px solid dodgerblue;position:relative;margin:2rem 0;text-align:center;line-height:0&quot;&gt;&lt;img src=&quot;/blogs/csslayout/kitten.jpg&quot; style=&quot;width:200px;padding:0;margin:0;display:inline&quot;/&gt;&lt;/div&gt;&lt;p&gt;If the line-height is 0, then the space below the baseline is also 0, so the whitespace is gone. If you have other text in the parent, that text will also be affected and could even start overlapping.&lt;/p&gt;&lt;h5 id=&quot;fixing-images&quot;&gt;&amp;#x27;Fixing&amp;#x27; images&lt;/h5&gt;&lt;p&gt;The easiest &amp;#x27;fix&amp;#x27; in this case it to simply &lt;em&gt;move the image off the baseline&lt;/em&gt;, for example by aligning it to the top or bottom of the line instead:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* or bottom */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:block;border:5px solid dodgerblue;position:relative;margin:2rem 0;text-align:center&quot;&gt;&lt;img src=&quot;/blogs/csslayout/kitten.jpg&quot; style=&quot;width:200px;padding:0;margin:0;display:inline;vertical-align:top&quot;/&gt;&lt;/div&gt;&lt;p&gt;The bottom of the image no longer sits on the baseline, so there is no longer whitespace reserved below it!&lt;/p&gt;&lt;p&gt;Now that we have discussed inline behaviour and understand how layout happens &lt;em&gt;inside&lt;/em&gt; boxes when they&amp;#x27;re in normal flow, lets move outside the box and see how the space between boxes works.&lt;/p&gt;&lt;h3 id=&quot;margin-behavior&quot;&gt;Margin behavior&lt;/h3&gt;&lt;p&gt;Margins are outside of an element and you can use them to push boxes away from each other.&lt;/p&gt;&lt;div class=&quot;box&quot; style=&quot;margin:1rem auto;position:relative;border:5px solid dodgerblue;width:200px;height:180px;outline:1rem solid #ff05&quot;&gt;&lt;/div&gt;&lt;div style=&quot;text-align:center;margin:1rem 0&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;margin:1rem auto;position:relative;border-left:1rem solid #ff05;border-right:1rem solid #ff05;display:inline;color:transparent;line-height:1;font-size:3.5rem;outline:5px solid dodgerblue;outline-offset:calc( -1rem - 5px);overflow-y:clip&quot;&gt;gr gerg dg dsgf d gz&lt;/div&gt;&lt;/div&gt;&lt;p&gt;For block elements, this happens in all directions. For inline elements, as we previously mentioned, it&amp;#x27;s the line they sit on that determines their placement. Adding margin to inline elemens will only apply in the inline direction (left and right).&lt;/p&gt;&lt;h4 id=&quot;margin-collapse&quot;&gt;Margin collapse&lt;/h4&gt;&lt;p&gt;When there are two block elements with margins stacked vertically and there are no other box parts (like border or padding) between them, the &lt;strong&gt;larger margin of the two is picked&lt;/strong&gt; and they &amp;#x27;collapse&amp;#x27; into each other.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.hotpink&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.dodgerblue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:1rem auto;position:relative&quot;&gt;&lt;div class=&quot;&quot; style=&quot;left:-20px;height:50px;position:relative;background:hotpink;margin-bottom:1rem&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;top:100%;height:1rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;&quot; style=&quot;height:50px;background:dodgerblue;margin-top:2rem;position:relative&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:2rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;We&amp;#x27;ve made the margins semi-transparent here so you can see how they actually overlap, or &amp;#x27;collapse&amp;#x27;, rather than add up.&lt;/p&gt;&lt;p&gt;This happens when any two elements in the same &lt;em&gt;formatting context&lt;/em&gt; (remember that one?) have margins that interact.&lt;/p&gt;&lt;h4 id=&quot;margin-collapse-between-parents-and-children&quot;&gt;Margin collapse between parents and children&lt;/h4&gt;&lt;p&gt;Elements and their children also share a formatting context, and that is why you also get margin collapse between parents and children.&lt;/p&gt;&lt;p&gt;You might add a margin to the first element inside a parent with the idea of pushing that element down from the top of the parent element, but due to margin collapse that margin is applied &lt;em&gt;outside of the parent&lt;/em&gt;, rather than pushing down the first element.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.parent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:4rem auto 1rem;position:relative&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue;position:relative&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:1rem&quot;&gt;&lt;/div&gt;&lt;div style=&quot;font-size:20px;margin:0;position:relative;text-align:center&quot;&gt;Hello there&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:3rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;You&amp;#x27;ll frequently want to prevent this, and while there is no explicit &lt;code class=&quot;language-text&quot;&gt;margin-collapse: none&lt;/code&gt; property, You can instead prevent margin collapse or create a new formatting context.&lt;/p&gt;&lt;p&gt;Firstly, adding a padding or a border, no matter how small, to the parent element will prevent margin collapse since the margins are no longer adjacent.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.parent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:4rem auto 1rem;position:relative&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue;position:relative;padding-top:1px&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:1rem&quot;&gt;&lt;/div&gt;&lt;div style=&quot;font-size:20px;margin-top:3rem;position:relative;text-align:center&quot;&gt;Hello there&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:3rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;And while this works, you can see that 1px of padding is affecting the layout. This might matter, or it might not.&lt;/p&gt;&lt;p&gt;another way is to use &lt;code class=&quot;language-text&quot;&gt;overflow: auto&lt;/code&gt; (or &lt;code class=&quot;language-text&quot;&gt;hidden&lt;/code&gt;) on the parent element. This creates a new formatting context &lt;em&gt;inside&lt;/em&gt; the element, and now the element and the child element are in different formatting contexts, which prevents margin collapse.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.parent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:4rem auto 1rem;position:relative&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;height:1rem&quot;&gt;&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue;position:relative;overflow:auto&quot;&gt;&lt;div style=&quot;font-size:20px;margin-top:3rem;position:relative;text-align:center&quot;&gt;Hello there&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:3rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Overflow auto will prevent other elements from being shown outside of the parent element, and when you have restrictions on the width or height of the element, it will show a scrollbar when the content overflows.&lt;/p&gt;&lt;p&gt;The explicit option is to define the parent as a new formatting context, which you can do with &lt;code class=&quot;language-text&quot;&gt;display: flow-root&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.parent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flow-root&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:4rem auto 1rem;position:relative&quot;&gt;&lt;div style=&quot;background:olive;opacity:0.2;height:1rem&quot;&gt;&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue;position:relative;display:flow-root&quot;&gt;&lt;div style=&quot;font-size:20px;margin-top:3rem;position:relative;text-align:center&quot;&gt;Hello there&lt;div style=&quot;background:olive;opacity:0.2;position:absolute;width:100%;bottom:100%;height:3rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;flow-root&lt;/code&gt; is a bit of a magical property that creates a new block formatting context without affecting other layout aspects of the element. It&amp;#x27;s a great way to prevent margin collapse without side effects.&lt;/p&gt;&lt;h4 id=&quot;negative-margins&quot;&gt;Negative margins&lt;/h4&gt;&lt;p&gt;Margins can also be negative. When you use negative margins you&amp;#x27;re not adding space between element, but you&amp;#x27;re changing the position of the element itself in a way that no longer interacts with the elements around it.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.top&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.overlap&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;width:50%;margin:1rem auto;position:relative&quot;&gt;&lt;div class=&quot;&quot; style=&quot;left:-20px;height:50px;position:relative;background:hotpink;margin-bottom:1rem&quot;&gt;&lt;div style=&quot;background:yellow;opacity:0.33;position:absolute;width:100%;top:100%;height:1rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;&quot; style=&quot;height:50px;background:dodgerblue;margin-top:-2rem;position:relative&quot;&gt;&lt;div style=&quot;background:yellow;opacity:0.33;position:absolute;width:100%;top:0%;height:2rem&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The way they get calculated is by taking the margin value, which can also be collapsed, see how the two margins overlap, then subtracting the negative value and starting the new element there.&lt;/p&gt;&lt;p&gt;Negative margins exist, but you&amp;#x27;re better off ignoring them and using positioning instead for overlapping layouts. (More on positioning later.)&lt;/p&gt;&lt;h4 id=&quot;centering-block-elements-with-margin-auto&quot;&gt;Centering block elements with margin: auto&lt;/h4&gt;&lt;p&gt;You can use margins to center elements horizontally by setting the left and right margins to &lt;code class=&quot;language-text&quot;&gt;auto&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%)&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;width:200px;margin-left:auto;margin-right:auto;background:dodgerblue;height:100px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This element gets centered because the layout for horizontal margins uses a &lt;strong&gt;constraint-based layout&lt;/strong&gt; when either width or margin is set to auto.&lt;/p&gt;&lt;p&gt;A constraint-based layout means that there is a formula that gets solved to determine the layout. The rule is:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;total width = margin-left + border-left + padding-left + width + padding-right + border-right + margin-right&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Because borders and paddings are fixed values we can leave them out and instead simplify it to this:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;available space to fill out = margin-left + width + margin-right&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Let&amp;#x27;s see what happens when we plug in different values for the width and margins into this formula.&lt;/p&gt;&lt;h5 id=&quot;case-1-everything-is-set-to-auto&quot;&gt;Case 1: everything is set to auto&lt;/h5&gt;&lt;p&gt;When all three values are set to &lt;code class=&quot;language-text&quot;&gt;auto&lt;/code&gt;, the width takes up all available space, and both margins are set to 0.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%)&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;width:auto;margin-left:auto;margin-right:auto;background:dodgerblue;height:100px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This is because the &amp;quot;preferred width&amp;quot; of a block element is 100%. So the equation becomes:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;total width = 0 + 100% + 0&lt;/p&gt;&lt;/blockquote&gt;&lt;h5 id=&quot;case-2-the-width-is-auto&quot;&gt;Case 2: the width is auto&lt;/h5&gt;&lt;p&gt;If you have values for both margins but aren&amp;#x27;t defining with, then the width takes up all remaining space between the two margins.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%)&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;width:auto;margin-left:2rem;margin-right:2rem;background:dodgerblue;height:100px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This is because the preferred width is still 100%, and the constraint then makes this formula:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;total width = 2rem + (100% - 4rem) + 2rem&lt;/p&gt;&lt;/blockquote&gt;&lt;h5 id=&quot;case-3-the-width-is-fixed&quot;&gt;Case 3: the width is fixed&lt;/h5&gt;&lt;p&gt;When the width is fixed and both margins are set to &lt;code class=&quot;language-text&quot;&gt;auto&lt;/code&gt;, the remaining space is divided equally between the two margins.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%)&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;width:50%;margin-left:auto;margin-right:auto;background:dodgerblue;height:100px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This is because the equation becomes:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;total width = 25% + 50% + 25%&lt;/p&gt;&lt;/blockquote&gt;&lt;h5 id=&quot;case-4-width-and-one-of-the-margins-are-fixed&quot;&gt;Case 4: width and one of the margins are fixed&lt;/h5&gt;&lt;p&gt;When the width is fixed and one of the margins is also set to a fixed value, the other margin is set to the remaining space.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%)&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;width:200px;margin-right:50px;margin-left:auto;background:dodgerblue;height:100px&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The equation becomes:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;total width = (100% - 250px) + 200px + 50px&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The browser calculates the remaining space as &lt;code class=&quot;language-text&quot;&gt;100% - 250px&lt;/code&gt; and adds it to the left margin, and so even though block elements normally start at the left edge of their container, this one is pushed to the right even though it doesn&amp;#x27;t have a specific value for &lt;code class=&quot;language-text&quot;&gt;margin-left&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;inline-block-the-hybrid&quot;&gt;Inline-block: The hybrid&lt;/h3&gt;&lt;p&gt;There&amp;#x27;s one last trick normal flow has: &lt;code class=&quot;language-text&quot;&gt;display: inline-block&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Given all the time we spent figuring out the differences between inline and block elements and how their specific behavior differs, what even is inline-block?&lt;/p&gt;&lt;p&gt;Inline-block tells the browser that an element behaves as &lt;strong&gt;inline for the formatting context around it&lt;/strong&gt;, but is &amp;amp;&lt;strong&gt;rendered as a block box&lt;/strong&gt;, creating its own formatting context.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.tag&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dodgerblue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 250px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%);text-align:center&quot;&gt;text text text&lt;div class=&quot;box&quot; style=&quot;display:inline-block;padding:5px 10px;margin:5px;background:dodgerblue;width:250px&quot;&gt; We have a bunch of text that wraps across lines.&lt;/div&gt;More text text text&lt;/div&gt;&lt;p&gt;You can see that the element sits inline with the rest of the text and you can even see that the text is placed on the same &lt;em&gt;baseline&lt;/em&gt;, but the element itself behaves as a block element, since it has a width, padding on all sides and the content inside of it wraps to multiple &lt;em&gt;lines&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;The CSS specification calls this behavior &amp;quot;shrink-to-fit&amp;quot;. Before Flexbox, this was a great way to render things like a list of tags or navigation items:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.nav-item&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dodgerblue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;margin:1rem auto;position:relative;background:color-mix(in srgb, yellow, transparent 80%);text-align:center&quot;&gt;&lt;div class=&quot;box&quot; style=&quot;display:inline-block;padding:5px 10px;margin:5px;background:dodgerblue&quot;&gt;Home&lt;/div&gt;&lt;div class=&quot;box&quot; style=&quot;display:inline-block;padding:5px 10px;margin:5px;background:dodgerblue&quot;&gt;About&lt;/div&gt;&lt;div class=&quot;box&quot; style=&quot;display:inline-block;padding:5px 10px;margin:5px;background:dodgerblue&quot;&gt;Contact&lt;/div&gt;&lt;/div&gt;&lt;h4 id=&quot;display-values-outer-and-inner&quot;&gt;Display values: outer and inner&lt;/h4&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;display&lt;/code&gt; property can have two parts: an outer and an inner value. &lt;code class=&quot;language-text&quot;&gt;inline-block&lt;/code&gt; as a value is from before this was introduced, but in &amp;quot;modern&amp;quot; CSS it&amp;#x27;s equivalent to:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline flow-root&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The outer value is how the element behaves in relation to other elements, so inline, and the inner value is how the element behaves in relation to its own content. We want that to have a block formatting context. You don&amp;#x27;t set that with &amp;quot;block&amp;quot;, but with &lt;code class=&quot;language-text&quot;&gt;flow-root&lt;/code&gt;. Think of it as &amp;quot;a new &amp;#x27;normal flow&amp;#x27; starting point&amp;quot;.&lt;/p&gt;&lt;p&gt;When you set a single item, that item is either for the inner or outer value depending on the value: some are inherently outer values, and some are inherently inner values. For example &lt;code class=&quot;language-text&quot;&gt;display: grid&lt;/code&gt; creates a grid for the content, and the outer value is inferred to be &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt;, making it the same as &lt;code class=&quot;language-text&quot;&gt;display: block grid&lt;/code&gt;. &lt;code class=&quot;language-text&quot;&gt;display: inline&lt;/code&gt; on the other hand is inherently an outer value, so the inner value is inferred to be &lt;code class=&quot;language-text&quot;&gt;flow&lt;/code&gt;, making it the same as &lt;code class=&quot;language-text&quot;&gt;display: inline flow&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;flow-root&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;flow&lt;/code&gt; are the two new keywords here and they are &lt;em&gt;roughly&lt;/em&gt; equivalent to &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;inline&lt;/code&gt;, but only for the inner value.&lt;/p&gt;&lt;h3 id=&quot;thats-normal-flow&quot;&gt;That&amp;#x27;s Normal Flow&lt;/h3&gt;&lt;p&gt;CSS&amp;#x27;s simplest layout algorithm! Would you have expected that there was so much behind something that you can essentially describe as &amp;quot;text goes from left to right, top to bottom&amp;quot;?&lt;/p&gt;&lt;p&gt;The good news is that all other layout algorithms build upon these concepts. In fact, unless you explicitly take an element &amp;quot;out of flow&amp;quot;, elements are still considered &amp;quot;in flow&amp;quot; regardless of what other layout algorithm you use, and the normal flow algorithm is used in addition to it.&lt;/p&gt;&lt;h4 id=&quot;key-takeaways-for-normal-flow&quot;&gt;Key takeaways for normal flow&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Block elements stack vertically (↓)&lt;/li&gt;&lt;li&gt;Inline elements flow horizontally (→)&lt;/li&gt;&lt;li&gt;Everything is a box&lt;/li&gt;&lt;li&gt;Anonymous boxes get created for text outside of elements&lt;/li&gt;&lt;li&gt;Margins collapse vertically&lt;/li&gt;&lt;li&gt;All inline elements sit on the baseline&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;other-layout-algorithms&quot;&gt;Other layout algorithms&lt;/h2&gt;&lt;p&gt;To get any other layout algorithm, you need to opt into them. There are three main layout algorithms in CSS:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Positioning&lt;/strong&gt; (relative, absolute, fixed, sticky)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Flexbox&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Grid&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There&amp;#x27;s a few more, like table layout, floats and multi-column, but the first two are hacks that we haven&amp;#x27;t needed in well over a decade, and multi-column is too niche for this already long article (and indeed, might soon be replaced by the masonry-style grid layout algorithm).&lt;/p&gt;&lt;h2 id=&quot;a-small-detour-sizes-in-percentages&quot;&gt;A small detour: sizes in percentages&lt;/h2&gt;&lt;p&gt;Before we dive into the other layout algorithms, we need to talk about percentages. When you use a percentage value for a size or a margin, padding etc, what is that percentage based on?&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 25%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10% 5%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5% 2.5%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Well, for &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;height&lt;/code&gt; (and their min and max and logical equivalents), percentages are based on the size of the &lt;strong&gt;containing block&lt;/strong&gt;. So 50% width is 50 percent of the width of the containing block, and 25% height is 25% of the height of the containing block.&lt;/p&gt;&lt;h3 id=&quot;hold-up-containing-block&quot;&gt;Hold up, containing block?&lt;/h3&gt;&lt;p&gt;So we have offset parents, stacking contexts and now also &amp;quot;containing block&amp;quot;.&lt;/p&gt;&lt;p&gt;The containing block is the box that contains the element. For most elements, this is the &lt;strong&gt;content-box&lt;/strong&gt; (remember the box model) of the first parent element that is &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;For &lt;code class=&quot;language-text&quot;&gt;position: absolute&lt;/code&gt; elements, it&amp;#x27;s the &lt;strong&gt;padding-box&lt;/strong&gt; of the closest stacking context, and for &lt;code class=&quot;language-text&quot;&gt;position: fixed&lt;/code&gt; elements, it&amp;#x27;s the viewport.&lt;/p&gt;&lt;p&gt;Those last two are exceptions but in practical use those exceptions actually result in elements sizing in ways you would expect.&lt;/p&gt;&lt;h3 id=&quot;percentages-for-margin-and-padding&quot;&gt;Percentages for margin and padding&lt;/h3&gt;&lt;p&gt;For margins and paddings, percentages are always based on the &lt;strong&gt;width&lt;/strong&gt; of the containing block, even for &lt;code class=&quot;language-text&quot;&gt;margin-top&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;padding-bottom&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s a bit confusing, but it&amp;#x27;s because of how the rest of CSS works. Most elements have a vertical size that changes based on their content: the more lines of text you add to a paragraph, the taller it gets.&lt;/p&gt;&lt;p&gt;If you were able to add a padding-top based on a percentage of the height, then that would increase the absolute height of the element, which in turn would increase the padding-top&amp;#x27;s absolute value, which would then update the height of the element and so on: it would create an infinite loop.&lt;/p&gt;&lt;p&gt;Instead, having percentages for vertical margins and paddings based on the &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt; of the containing block gives you a predictable value that doesn&amp;#x27;t change when it causes the height of an element to increase.&lt;/p&gt;&lt;h2 id=&quot;positioning-schemes&quot;&gt;Positioning schemes&lt;/h2&gt;&lt;p&gt;With positioning, we can tell the browser how to place an element either in relation to its calculated flow position, or explicitly take it out of normal flow. It&amp;#x27;s also where the &lt;strong&gt;third dimension&lt;/strong&gt; is introduced with &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt;. That concept doesn&amp;#x27;t exist in normal flow, and adding z-index to an element without a position does nothing.&lt;/p&gt;&lt;p&gt;Elements by default have &lt;code class=&quot;language-text&quot;&gt;position: static&lt;/code&gt;, which means no explicit positioning. To activate the positioning layout algorithm, pick a different value:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; (the default)&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;sticky&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Each of these are their own little layout algorithm, so let&amp;#x27;s go through them one by one.&lt;/p&gt;&lt;h3 id=&quot;position-relative&quot;&gt;Position: relative&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt; is the basic value that lets you opt in to positioning layout. A relatively positioned element has a box placed in the same spot as normal flow, but you can move the element away from that box using &lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bottom&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;right&lt;/code&gt;, which are called &lt;strong&gt;inset properties&lt;/strong&gt;. There&amp;#x27;s also a &lt;code class=&quot;language-text&quot;&gt;inset&lt;/code&gt; shorthand property that lets you set all four inset properties at once.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.box&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;line-height:1;text-align:center;font-size:2rem;margin:1rem 0 2rem&quot;&gt;text text text&lt;div style=&quot;background:#0002;width:100px;height:100px;margin:0 auto&quot;&gt;&lt;div style=&quot;background:dodgerblue;width:100px;height:100px;position:relative;top:40px;left:40px&quot;&gt;&lt;/div&gt;&lt;/div&gt;text text text&lt;/div&gt;&lt;p&gt;Notice that the original box is the one that takes up space and pushes the text away, but the element itself is elsewhere. The original box is used for the rest of the layout.&lt;/p&gt;&lt;p&gt;This pushing away of the element from it&amp;#x27;s original box is something you&amp;#x27;ll rarely want, or need. What you mainly use &lt;code class=&quot;language-text&quot;&gt;position: relative&lt;/code&gt; for is to &lt;strong&gt;make an element an offset parent&lt;/strong&gt; for absolutely positioned children. Before we get into what an offset parent is, let&amp;#x27;s explain &lt;code class=&quot;language-text&quot;&gt;position: absolute&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;position-absolute&quot;&gt;Position: absolute&lt;/h3&gt;&lt;p&gt;Where elements with &lt;code class=&quot;language-text&quot;&gt;position: relative&lt;/code&gt; are still in normal flow, &lt;code class=&quot;language-text&quot;&gt;position: absolute&lt;/code&gt; elements are not. This means they do not interact with other elements: they don&amp;#x27;t take up space and don&amp;#x27;t push other elements out of the way.&lt;/p&gt;&lt;p&gt;Inset properties don&amp;#x27;t move them away from their initial box like &lt;code class=&quot;language-text&quot;&gt;relative&lt;/code&gt;, they instead position the element in relation to the offset parent.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.box&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue;line-height:1;text-align:center;align-content:center;font-size:2rem;margin:1rem 0 2rem;position:relative;height:200px&quot;&gt;&lt;p&gt;text text text&lt;/p&gt;&lt;div style=&quot;background:dodgerblue;width:100px;height:100px;position:absolute;top:40px;right:40px&quot;&gt;&lt;/div&gt;&lt;p&gt;text text text&lt;/p&gt;&lt;/div&gt;&lt;h4 id=&quot;the-offset-parent&quot;&gt;The offset parent&lt;/h4&gt;&lt;p&gt;The offset parent is the first element in the list of ancestors that is itself also positioned (any position value other than &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt;). By default, the &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; is the only offset parent. So &lt;code class=&quot;language-text&quot;&gt;position: absolute; top: 0;&lt;/code&gt; will place an element at the top of the page.&lt;/p&gt;&lt;p&gt;If you add positioning to any parent element, it becomes an offset parent, and now &lt;code class=&quot;language-text&quot;&gt;top: 0&lt;/code&gt; no longer means the top of the body element, but of that parent element.&lt;/p&gt;&lt;p&gt;Any element that is positioned will become an offset parent, so you can have many offset parents inside of other offset parents. You&amp;#x27;re always positioning against the nearest offset parent up the DOM tree.&lt;/p&gt;&lt;h5 id=&quot;finding-the-offset-parent&quot;&gt;Finding the offset parent&lt;/h5&gt;&lt;p&gt;Layout issues where your absolutely positioned element isn&amp;#x27;t where you expect it are almost always because what you think the offset parent is is different from what the browser thinks.&lt;/p&gt;&lt;p&gt;To find the offset parent in Chrome, Safari, Firefox etc&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Find the element in the element inspector&lt;/li&gt;&lt;li&gt;Switch to the console panel&lt;/li&gt;&lt;li&gt;Type &lt;code class=&quot;language-text&quot;&gt;$0.offsetParent&lt;/code&gt; and hit enter&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;, you can see the offset parent directly in the element inspector&amp;#x27;s debug panel.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Find the element in the &lt;a href=&quot;/docs/elements-panel/&quot;&gt;element inspector&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Switch to the debug tab&lt;/li&gt;&lt;li&gt;See Offset parent under &amp;quot;contexts&amp;quot;&lt;/li&gt;&lt;/ul&gt;&lt;img alt=&quot;Element debug panel with an arrow pointing to the offset parent&quot; src=&quot;static/offsetpanel1-d160382b5eeab2d92447220699441dd7.png&quot; class=&quot;imgshadow&quot; style=&quot;margin:1rem auto 1rem;display:block;max-width:100%&quot;/&gt;&lt;p&gt;Learn more about that in our dedicated article on &lt;a href=&quot;/blog/offset-parent-and-stacking-context-positioning-elements-in-all-three-dimensions/#offset-parent&quot;&gt;Offset parent and stacking context&lt;/a&gt; (more on that second one later).&lt;/p&gt;&lt;h3 id=&quot;position-fixed&quot;&gt;Position: fixed&lt;/h3&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;position: fixed&lt;/code&gt; is a lot like &lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt;. Elements are taken out of the normal flow, but in this scheme, the offsetParent is always the viewport, and inset properties are used to position from the edge of it.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That means that while absolutely positioned elements scroll along with the rest of the page, &lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt; elements will always stay in view.&lt;/p&gt;&lt;h3 id=&quot;position-sticky&quot;&gt;Position: sticky&lt;/h3&gt;&lt;p&gt;The last scheme is &lt;code class=&quot;language-text&quot;&gt;position: sticky&lt;/code&gt;. It was added to CSS much later than the other ones, and it&amp;#x27;s for elements that you like to keep in view for some part of the time, but still want to scroll along with their offset parent.&lt;/p&gt;&lt;p&gt;With &lt;code class=&quot;language-text&quot;&gt;position:sticky&lt;/code&gt;, the element will be placed the same way as &lt;code class=&quot;language-text&quot;&gt;position: relative&lt;/code&gt;, but the top/left/bottom/right values don&amp;#x27;t move the element. Instead they determine where an element gets stuck relative to the scrolling context, which is usually the viewport.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.sticky-header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sticky&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;height:20vh;text-align:center;border:5px solid hotpink&quot;&gt;&lt;div class=&quot;sticky-header&quot; style=&quot;position:sticky;top:50%;background:dodgerblue;width:250px;height:50px;margin:0 auto&quot;&gt;Scroll down to see me stick&lt;/div&gt;&lt;/div&gt;&lt;p&gt;As an element scrolls it will behave like a relatively positioned element, until it hits that inset value where it gets stuck.&lt;/p&gt;&lt;p&gt;Then it will stay stuck, until the offset parent&amp;#x27;s bottom catches up with it, at which point the element will get unstuck and continue scrolling with it&amp;#x27;s offset parent.&lt;/p&gt;&lt;p&gt;A tricky thing here is that it will only get stuck if the browser determines it &lt;em&gt;can&lt;/em&gt; get stuck. To learn more about debugging sticky positioning, check out our article: &lt;a href=&quot;/blog/getting-stuck-all-the-ways-position-sticky-can-fail/#main&quot;&gt;Getting stuck: all the ways position sticky can fail&lt;/a&gt;&lt;/p&gt;&lt;h2 id=&quot;overview-of-positioning-schemes&quot;&gt;Overview of positioning schemes&lt;/h2&gt;&lt;p&gt;So to compare the different positioning schemes, here&amp;#x27;s a quick overview:&lt;/p&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Position&lt;/th&gt;&lt;th&gt;In Flow?&lt;/th&gt;&lt;th&gt;Offset Parent&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;static&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Not applicable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;relative&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Nearest positioned&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;absolute&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Nearest positioned&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;fixed&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Viewport&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;sticky&lt;/td&gt;&lt;td&gt;Yes*&lt;/td&gt;&lt;td&gt;Scrollport&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;* until it gets stuck, then it becomes like fixed.&lt;/p&gt;&lt;div class=&quot;threed&quot;&gt;&lt;/div&gt;&lt;h2 id=&quot;the-stacking-context&quot;&gt;The stacking context&lt;/h2&gt;&lt;p&gt;Remember how I said that with positioning, we introduce the third dimension? Well we haven&amp;#x27;t talked about that yet.&lt;/p&gt;&lt;p&gt;While the offset parent tells you how an element is positioned in the x and y directions, the &lt;strong&gt;stacking context&lt;/strong&gt; tells you how it&amp;#x27;s positioned in the third dimension, the z-axis. Or in other words, how close it is to the &amp;quot;front of the screen&amp;quot;. And like offset parents, you have stacking contexts inside stacking contexts.&lt;/p&gt;&lt;p&gt;The default stacking context is not the &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; element, but the &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; element. So all elements are stacked in relation to the &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; element by default.&lt;/p&gt;&lt;h3 id=&quot;default-stacking-order&quot;&gt;Default stacking order&lt;/h3&gt;&lt;p&gt;Elements by default have a stacking order that&amp;#x27;s the same as source order and&lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; lets you reorder them.&lt;/p&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hotpink&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;div::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rebeccapurple&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;threed threed2&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Flip them around: */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;div::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;div::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;threed threed2 threed3&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;reordering-happens-inside-the-stacking-context&quot;&gt;Reordering happens inside the stacking context&lt;/h3&gt;&lt;p&gt;In the example below, the purple element has a &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; of 99, which is (much) higher than the blue element&amp;#x27;s &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; of just 2. So you&amp;#x27;d expect the purple element to be in front of the blue one. However, because the purple element is inside of the pink element with a &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; of 1, it&amp;#x27;s stuck inside the stacking context of the pink element.&lt;/p&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.first&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hotpink&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.inner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rebeccapurple&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 99&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.second&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dodgerblue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;stack&quot;&gt;&lt;div class=&quot;stack1&quot;&gt;1&lt;div class=&quot;inner&quot;&gt; 99 &lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;stack2&quot;&gt;2 &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So instead of 99 being more than 2, you should actually think of it as (1,99) which &lt;em&gt;is&lt;/em&gt; less than 2. No matter how high your z-index is, it will never win from elements in a stacking context with a higher z-index than the stacking context you&amp;#x27;re currently in.&lt;/p&gt;&lt;h3 id=&quot;creating-a-stacking-context&quot;&gt;Creating a stacking context&lt;/h3&gt;&lt;p&gt;There&amp;#x27;s just a &lt;em&gt;few&lt;/em&gt; CSS properties that can create a stacking context:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt; of &lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;sticky&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; other than &lt;code class=&quot;language-text&quot;&gt;auto&lt;/code&gt; (with a &lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt; other than &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; or when in a flex or grid layout)&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; less than 1&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;mix-blend-mode&lt;/code&gt; other than normal&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;backdrop-filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;perspective&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;clip-path&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mask&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mask-image&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;mask-box-image&lt;/code&gt; other than none&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;isolation&lt;/code&gt; set to isolate&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;container-type&lt;/code&gt; set to size or inline-size (so any container element)&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; property set to &lt;code class=&quot;language-text&quot;&gt;mix-blend-mode&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;perspective&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;clip-path&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mask&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mask-image&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;mask-box-image&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;contain&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;layout&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;paint&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;strict&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;… This makes &lt;code class=&quot;language-text&quot;&gt;offsetParent&lt;/code&gt; sound easy right? It&amp;#x27;s quite a lot if you list them all out. Most of these properties aren&amp;#x27;t used very often though.&lt;/p&gt;&lt;h4 id=&quot;the-most-common-ones-are&quot;&gt;The most common ones are:&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4 id=&quot;explicit-stacking-context&quot;&gt;Explicit stacking context&lt;/h4&gt;&lt;p&gt;Out of the entire list, &lt;code class=&quot;language-text&quot;&gt;isolation: isolate&lt;/code&gt; is the most explicit way of indicating in your CSS that you&amp;#x27;re creating a new stacking context:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;isolation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isolate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;a-practical-stacking-context-example&quot;&gt;A practical stacking context example&lt;/h3&gt;&lt;p&gt;Here&amp;#x27;s a practical example, a simple block quote design with a decorative quote mark. After positioning the quote mark correctly in the X and Y axis we can now see that the quote mark is in front of the text. Because the text itself isn&amp;#x27;t an element (it&amp;#x27;s an anonymous box), we can&amp;#x27;t bring it forward using &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt;.&lt;/p&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div style=&quot;flex:1 1 auto&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;blockquote&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* offset parent */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;blockquote span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;blockquote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token entity named-entity&quot; title=&quot;“&quot;&gt;&amp;amp;ldquo;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  Lorem ipsum dolor sit...
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;blockquote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;position:relative;align-content:center;flex:1 1 auto&quot;&gt;&lt;div class=&quot;quote&quot;&gt;&lt;blockquote&gt;&lt;span&gt;“&lt;/span&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora tenetur molestias consequatur, quidem nam ipsum minima aliquid recusandae delectus enim.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Instead, we need to bring the quote backwards with &lt;code class=&quot;language-text&quot;&gt;z-index: -1&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div style=&quot;flex:1 1 auto&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;blockquote span&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;position:relative;align-content:center;flex:1 1 auto&quot;&gt;&lt;div class=&quot;quote&quot;&gt;&lt;blockquote&gt;&lt;span style=&quot;z-index:-1&quot;&gt;“&lt;/span&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora tenetur molestias consequatur, quidem nam ipsum minima aliquid recusandae delectus enim.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;When you do that however, the quote mark disappears behind the background of the blockquote. That&amp;#x27;s because the current stacking context is still the HTML element. So we need to make the parent element the stacking context.&lt;/p&gt;&lt;div class=&quot;code-and-demo&quot;&gt;&lt;div style=&quot;flex:1 1 auto&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;blockquote&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;isolation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; isolate&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;position:relative;align-content:center;flex:1 1 auto&quot;&gt;&lt;div class=&quot;quote&quot;&gt;&lt;blockquote style=&quot;isolation:isolate&quot;&gt;&lt;span style=&quot;z-index:-1&quot;&gt;“&lt;/span&gt;Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora tenetur molestias consequatur, quidem nam ipsum minima aliquid recusandae delectus enim.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The blockquote is now a new stacking context, and the quote mark can no longer go behind the background. Instead, it sits at the beginning of the stacking context inside the blockquote: behind the text but in front of the background.&lt;/p&gt;&lt;h3 id=&quot;finding-the-stacking-context&quot;&gt;Finding the stacking context&lt;/h3&gt;&lt;p&gt;Finding the stacking context of an element is not as easy as finding the offset parent. Unlike &lt;code class=&quot;language-text&quot;&gt;element.offsetParent&lt;/code&gt;, there is no &lt;code class=&quot;language-text&quot;&gt;element.stackingContext&lt;/code&gt; property.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s why we built a debug panel into Polypane that not only shows you the offset parent, but also the stacking context of the selected element, along with whether the element itself creates a stacking context. It&amp;#x27;s very neat, and you can learn more about it in our article on &lt;a href=&quot;/blog/offset-parent-and-stacking-context-positioning-elements-in-all-three-dimensions/#stacking-context&quot;&gt;Offset parent and stacking context&lt;/a&gt;.&lt;/p&gt;&lt;img alt=&quot;Element debug panel with an arrow pointing to the stacking context parent&quot; src=&quot;static/offsetpanel2-e978d36518ac4ea630f4b5e44e8a119f.png&quot; class=&quot;imgshadow&quot; style=&quot;margin:1rem auto 1rem;display:block;max-width:100%&quot;/&gt;&lt;h4 id=&quot;the-polypane-debug-panel&quot;&gt;The Polypane debug panel&lt;/h4&gt;&lt;p&gt;The top section in the debug panel shows you CSS attributes that commonly cause issues, like &lt;code class=&quot;language-text&quot;&gt;display&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Below that, it shows the different contexts that the element is in: the offset parent, the containing block and the stacking context.&lt;/p&gt;&lt;p&gt;Lastly, it shows whether the element itself is an offset parent, containing block or stacking context.&lt;/p&gt;&lt;img src=&quot;static/debugpanel-f7262de83ecb203b837d47f43224d7a3.png&quot; alt=&quot;The debug tab in the Elements panel&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;These three sections make it very easy to figure out what&amp;#x27;s going on with the layout of an element, and really speed up your CSS layout debugging.&lt;/p&gt;&lt;h2 id=&quot;grid-and-flexbox&quot;&gt;Grid and Flexbox&lt;/h2&gt;&lt;p&gt;That leaves Grid and Flexbox. I&amp;#x27;m not going to spend any time on the syntax or specific features of either layout algorithm, instead focusing on how they interact with the previous layout algorithms and concepts like offset parent and stacking context.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;For Flexbox, check out &lt;a href=&quot;https://every-layout.dev/&quot;&gt;Every Layout&lt;/a&gt; by &lt;a href=&quot;https://heydonworks.com/&quot;&gt;Heydon Pickering&lt;/a&gt; and &lt;a href=&quot;https://piccalil.li/&quot;&gt;Andy Bell&lt;/a&gt; or &lt;a href=&quot;https://www.flexboxsimplified.com&quot;&gt;Flexbox Simplified&lt;/a&gt; by &lt;a href=&quot;https://www.kevinpowell.co/&quot;&gt;Kevin Powell&lt;/a&gt;. For Grid, check out &lt;a href=&quot;https://gridbyexample.com/&quot;&gt;Grid by Example&lt;/a&gt; by &lt;a href=&quot;https://rachelandrew.co.uk/&quot;&gt;Rachel Andrew&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Both Flex and Grid are significantly different from the layout algorithms that came before. That&amp;#x27;s because they were developed not as a way to lay out web documents, but as a way to lay out &lt;strong&gt;web apps&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;the-key-differences&quot;&gt;The key differences&lt;/h3&gt;&lt;p&gt;When you look at Flex and Grid, they each approach layout from a different direction.&lt;/p&gt;&lt;div style=&quot;display:grid;grid-template-columns:repeat(2, min(250px, 1fr));grid-auto-rows:auto;gap:1rem;padding:1rem 2rem;margin:0 auto;max-width:50em;justify-content:center&quot;&gt;&lt;div&gt;&lt;h4 id=&quot;flexbox-is-bottom-up&quot;&gt;&lt;strong&gt;Flexbox&lt;/strong&gt; is &amp;quot;bottom up&amp;quot;&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Get a bunch of elements&lt;/li&gt;&lt;li&gt;Order them in a row or column&lt;/li&gt;&lt;li&gt;Distribute them according to rules you provide&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;h4 id=&quot;grid-is-top-down&quot;&gt;&lt;strong&gt;Grid&lt;/strong&gt; is &amp;quot;top down&amp;quot;&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Create a bunch of areas&lt;/li&gt;&lt;li&gt;Determine their sizes and positions of those areas according to rules you provide&lt;/li&gt;&lt;li&gt;Place elements inside those areas&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;So Flexbox starts with the elements you have and distributed them, while Grid starts with defining the available spaces and then adding the elementes to them.&lt;/p&gt;&lt;p&gt;Additionally, Flex is fundamentally one-dimensional (though it can wrap to a next line) while Grid is two-dimensional.&lt;/p&gt;&lt;h3 id=&quot;flex-and-grid-versus-normal-flow&quot;&gt;Flex and Grid versus normal flow&lt;/h3&gt;&lt;p&gt;Both are replacements of sorts for normal flow. They reason about the distribution of multiple elements in a specific way, rather than the positioning of a single element in relation to other elements.&lt;/p&gt;&lt;p&gt;A very big difference between Flex and Grid on one hand and previous positioning on the other hand is that Flex and Grid no longer depend on font properties. The layouts you make and their alignment are based on the generated boxes, not on the font properties of the elements.&lt;/p&gt;&lt;p&gt;This makes a lot of things much simpler.&lt;/p&gt;&lt;p&gt;Both also try to make sure that regardless of the elements inside of them, the layout will be adhered to. In Flexbox this means that if an element has a fixed &lt;code class=&quot;language-text&quot;&gt;width&lt;/code&gt;, but that width doesn&amp;#x27;t fit in the space the Flex container has available for it, the elements size will be overwritten to make it fit the Flex layout. In Grid, an elements width and height are constrained by the grid area they&amp;#x27;re placed in.&lt;/p&gt;&lt;h3 id=&quot;flex-and-grid-combined-with-positioning&quot;&gt;Flex and Grid combined with positioning&lt;/h3&gt;&lt;p&gt;Both &lt;code class=&quot;language-text&quot;&gt;flex&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;grid&lt;/code&gt; are &lt;code class=&quot;language-text&quot;&gt;display&lt;/code&gt; properties, separate from the &lt;code class=&quot;language-text&quot;&gt;position&lt;/code&gt; property. This means that the way to determine offset parents and stacking contexts doesn&amp;#x27;t change.&lt;/p&gt;&lt;p&gt;You can take an element out of a flex or grid layout with &lt;code class=&quot;language-text&quot;&gt;position: absolute&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;position: fixed&lt;/code&gt;. Likewise, each element inside a flex or grid container, including the container itself, can be made an offset parent with &lt;code class=&quot;language-text&quot;&gt;position: relative&lt;/code&gt; or a stacking context with &lt;code class=&quot;language-text&quot;&gt;isolation: isolate&lt;/code&gt;.&lt;/p&gt;&lt;h3 id=&quot;what-not-being-dependent-on-font-properties-means&quot;&gt;What not being dependent on font properties means&lt;/h3&gt;&lt;p&gt;Unline &lt;code class=&quot;language-text&quot;&gt;vertical-align: middle&lt;/code&gt; which uses the line-height and font properties (as mentioned above) to determine the middle of a &lt;em&gt;line&lt;/em&gt;, both flex and grid align based on the boxes they create.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;align-items: center&lt;/code&gt; in both layout algorithms will align the boxes of the elements in the middle of the flex or grid boxes, regardless of the font properties or line height of the elements.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.grid,
.flex {
 align-items: center;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:flex;gap:1rem;justify-content:center&quot;&gt;&lt;div style=&quot;display:flex;border:5px solid dodgerblue;align-items:center;height:150px;gap:1rem;padding:1rem&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item1&quot;&gt;1&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item2&quot;&gt;2&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item3&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;display:grid;grid-template-columns:1fr 1fr 1fr;border:5px solid dodgerblue;align-items:center;height:150px;gap:1rem;padding:1rem&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item1&quot;&gt;1&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item2&quot;&gt;2&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot; class=&quot;item3&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;This new type of alignment logic is the same in Flex and grid and it worked so well that it was actually added to &lt;code class=&quot;language-text&quot;&gt;display: block&lt;/code&gt; as well.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;align-content&lt;/code&gt; property lets you vertically center content in block elements, as long as they have a determined height. It was added to CSS Box Alignment level 3 and works across browsers.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;align-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:flex;gap:1rem;justify-content:space-evenly&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;block&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;1&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;2&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;3&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;position:relative;display:block;border:5px solid dodgerblue;align-content:center;height:300px;width:200px;text-align:center;margin:auto&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot;&gt;1&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot;&gt;2&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot;&gt;3&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4 id=&quot;-items-and--content&quot;&gt;*-items and *-content&lt;/h4&gt;&lt;p&gt;Flex and grid both have &lt;code class=&quot;language-text&quot;&gt;align-items&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;align-content&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;justify-content&lt;/code&gt; properties. Grid also has &lt;code class=&quot;language-text&quot;&gt;justify-items&lt;/code&gt;. So what is &lt;code class=&quot;language-text&quot;&gt;items&lt;/code&gt; and what is &lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt;?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;*-items&lt;/code&gt; properties align the individual items, for example inside a grid area.&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;*-content&lt;/code&gt; properties align the items as a group (usually as a row or column).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the example below, we have a simple grid that has repeating rows of 50px high, and three 1fr columns. In the first/left example, we have &lt;code class=&quot;language-text&quot;&gt;align-items: end&lt;/code&gt; which aligns the items to the &lt;strong&gt;end of their grid area&lt;/strong&gt;. In the second example, we set &lt;code class=&quot;language-text&quot;&gt;align-content: end&lt;/code&gt;. This keeps the items the full size of their grid area, but aligns the entire group of items to the end of the container&lt;/p&gt;&lt;div style=&quot;display:flex;gap:1rem;justify-content:space-evenly&quot;&gt;&lt;div class=&quot;grid-css-layout-demo align-items-end&quot;&gt;&lt;div&gt;1&lt;/div&gt;&lt;div&gt;2&lt;/div&gt;&lt;div&gt;3&lt;/div&gt;&lt;div&gt;4&lt;/div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;grid-css-layout-demo align-content-end&quot;&gt;&lt;div&gt;1&lt;/div&gt;&lt;div&gt;2&lt;/div&gt;&lt;div&gt;3&lt;/div&gt;&lt;div&gt;4&lt;/div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3 id=&quot;grid-and-position-sticky&quot;&gt;Grid and position: sticky&lt;/h3&gt;&lt;p&gt;Though the way to determine offset parents and stacking contexts doesn&amp;#x27;t change with Flex and Grid, when you combine Grid and position: sticky, that won&amp;#x27;t work as you might expect.&lt;/p&gt;&lt;p&gt;The reason for that is the default value that grid has for &lt;code class=&quot;language-text&quot;&gt;align-items&lt;/code&gt;, which is &lt;code class=&quot;language-text&quot;&gt;stretch&lt;/code&gt;. That means that the element with position: sticky already is as tall as the grid area and that means that when it reaches that inset value to get stuck, the bottom of the element is already at the bottom of the grid area and so it never gets stuck.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s easy to miss that, because elements in a grid area without a background or border look like they are not filling the entire area.&lt;/p&gt;&lt;p&gt;An element doesn&amp;#x27;t &lt;em&gt;have&lt;/em&gt; to fill the entire grid area though. You can set &lt;code class=&quot;language-text&quot;&gt;align-items: start&lt;/code&gt; on the grid container to have all items align to the start of their grid area instead of stretching to fill it, or &lt;code class=&quot;language-text&quot;&gt;align-self: start&lt;/code&gt; on the element itself&lt;/p&gt;&lt;div style=&quot;display:flex;gap:1rem;justify-content:space-evenly&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.grid-container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/* the default */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; stretch&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sticky&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;.start&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;align-self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;display:grid;grid-template-columns:1fr  1fr;border:5px solid dodgerblue;height:350px;gap:1rem;padding:1rem;justify-content:center;margin:2rem auto&quot;&gt;&lt;div style=&quot;position:sticky;top:50%&quot;&gt;stretch&lt;/div&gt;&lt;div style=&quot;position:sticky;top:50%;align-self:start&quot;&gt;start&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Check out our article: &lt;a href=&quot;/blog/getting-stuck-all-the-ways-position-sticky-can-fail/#main&quot;&gt;Getting stuck: all the ways position sticky can fail&lt;/a&gt; for more on this.&lt;/p&gt;&lt;h3 id=&quot;the-final-boss-centering-a-div&quot;&gt;The final boss: centering a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;An age old joke about CSS is that it&amp;#x27;s impossible to center a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; vertically.&lt;/p&gt;&lt;p&gt;Now that you know the fundamentals, you know that&amp;#x27;s not true.&lt;/p&gt;&lt;p&gt;The reason vertical centering is &amp;#x27;hard&amp;#x27; is because CSS is built on normal flow, where the height of elements is determined by their content.&lt;/p&gt;&lt;p&gt;If the height is determined by the content, and the content is a single line of text, then the height of the element is the height of that line of text. It&amp;#x27;s already vertically centered, it&amp;#x27;s just not the height you want.&lt;/p&gt;&lt;p&gt;With that knowledge, there are actually dozens of ways to vertically center a &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. But modern CSS gives us one way that&amp;#x27;s definitely the simplest: &lt;code class=&quot;language-text&quot;&gt;place-content&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;place-content&lt;/code&gt; is a shorthand for the combination of &lt;code class=&quot;language-text&quot;&gt;align-content&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;justify-content&lt;/code&gt; and as we mentioned earlier in the article, that works in Flex, Grid &lt;strong&gt;and Block elements&lt;/strong&gt;. For it to work, your element does have to have a defined height (because if not, the contents define the height).&lt;/p&gt;&lt;div style=&quot;display:flex;gap:1rem;justify-content:space-evenly&quot;&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;place-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;border:5px solid dodgerblue;height:200px;place-content:center;width:200px;margin:2rem auto;padding:2rem&quot;&gt;&lt;div style=&quot;border:5px solid dodgerblue&quot;&gt;I&amp;#x27;m centered!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;That&amp;#x27;s all it takes. Vertically centering is trivial.&lt;/p&gt;&lt;h2 id=&quot;things-we-left-out&quot;&gt;Things we left out&lt;/h2&gt;&lt;p&gt;The purpose of this article is to explain the fundamentals of CSS layout, but there are more concepts that can help you understand your layouts that we didn&amp;#x27;t cover:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Content sizing&lt;/strong&gt;: how &lt;code class=&quot;language-text&quot;&gt;min-content&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;max-content&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;fit-content&lt;/code&gt; work, and how intrinsic sizing works.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Container queries&lt;/strong&gt;: how they create new layout contexts.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Aspect-ratio&lt;/strong&gt;: how it affects sizing of elements.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Subgrid&lt;/strong&gt;: how it works and how it relates to grid.&lt;/li&gt;&lt;li&gt;Honestly, a deeper dive into both &lt;strong&gt;Flexbox&lt;/strong&gt; and &lt;strong&gt;Grid&lt;/strong&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Each of these topics deserve their own article and we might cover them in the future.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;When you get started with CSS layout, it can feel overwhelming. There are a lot of concepts to learn, beyond the mere syntax and available properties. If you don&amp;#x27;t take the time to understand those concepts, you&amp;#x27;ll end up always fighting or guessing your way around designing pages.&lt;/p&gt;&lt;p&gt;CSS is built on a set of small, logical concepts that each are easy to understand, from &amp;quot;text goes left to right, top to bottom&amp;quot; to &amp;quot;everything is a box&amp;quot; to &amp;quot;elements can be taken outside of flow&amp;quot;. Each layout algorithm builds upon these concepts, adding new ways to position and align elements to handle more and more complex situations. But the building blocks remain the same.&lt;/p&gt;&lt;p&gt;Understanding these building blocks of layout makes it easier to reason about CSS, to visualize how the browser is going to lay out your page and how to fix layout issues when they arise.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Not All Browser APIs Are "Web" APIs]]></title><description><![CDATA[When we talk about "the web platform", we often treat it as a unified, standards-based system: browsers implement features from the same…]]></description><link>https://polypane.app/blog/not-all-browser-apis-are-web-apis/</link><guid isPermaLink="false">https://polypane.app/blog/not-all-browser-apis-are-web-apis/</guid><pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When we talk about &amp;quot;the web platform&amp;quot;, we often treat it as a unified, standards-based system: browsers implement features from the same specifications, even if they do so in different time frames. That should mean that when they&amp;#x27;re implemented, we can rely on APIs to work the same across browsers, and definitely across browsers using the same rendering engine.&lt;/p&gt;&lt;p&gt;But dig a little deeper, and you&amp;#x27;ll find that many APIs we rely on aren&amp;#x27;t actually part of &amp;quot;the web&amp;quot; at all. They&amp;#x27;re standardized interfaces, but with browser-specific implementations that depend on third-party services, proprietary systems, or vendor-specific infrastructure.&lt;/p&gt;&lt;h2 id=&quot;what-that-means-in-practice&quot;&gt;What that means in practice&lt;/h2&gt;&lt;p&gt;For example, to &lt;a href=&quot;https://polypane.app/docs/browser-features/#geolocation-api&quot;&gt;use the Geolocation API&lt;/a&gt; in Polypane you need to add a Google API key. The same API works fine in Chrome without any configuration, so are we just being annoying?&lt;/p&gt;&lt;p&gt;Actually, the Geolocation API in Chromium uses Google&amp;#x27;s online geolocation services. While Google Chrome obviously gets to use Google&amp;#x27;s services for free, other browsers don&amp;#x27;t.&lt;/p&gt;&lt;p&gt;When we tell people this they&amp;#x27;re often surprised. Surely &lt;strong&gt;web standards&lt;/strong&gt; wouldn&amp;#x27;t rely on proprietary services?&lt;/p&gt;&lt;p&gt;Yet they do, and when you think about it, that&amp;#x27;s even a little insidious. No where does the browser tell you that by using the Geolocation API, you&amp;#x27;re potentially sending your users&amp;#x27; location data to Google (or Apple, depending on the browser).&lt;/p&gt;&lt;p&gt;So let&amp;#x27;s go over these APIs that are available in (some) browsers, but aren&amp;#x27;t actually part of the web platform in the way you and I think about &amp;#x27;the web&amp;#x27;.&lt;/p&gt;&lt;h2 id=&quot;the-illusion-of-standardization&quot;&gt;The illusion of standardization&lt;/h2&gt;&lt;p&gt;Let&amp;#x27;s zoom in on that &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API&quot;&gt;Geolocation API&lt;/a&gt;. It looks like a clean, standardized web API:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;geolocation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coords&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latitude&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;coords&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;longitude&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Geolocation failed:&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The interface is standardized. The specification is maintained by the W3C. The docs on MDN are excellent. But the actual implementation? That&amp;#x27;s entirely up to the browser vendor.&lt;/p&gt;&lt;h3 id=&quot;where-does-your-location-actually-come-from&quot;&gt;Where does your location actually come from?&lt;/h3&gt;&lt;p&gt;Browsers don&amp;#x27;t magically know where you are. Behind that clean API, they&amp;#x27;re making decisions about data sources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;OS service/GPS&lt;/strong&gt;: On mobile devices, accessing the operating system&amp;#x27;s location services (often using GPS).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Wi-Fi positioning&lt;/strong&gt;: Your browser sends nearby Wi-Fi access point MAC addresses to a location service provider. They&amp;#x27;re hashed first, but still.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;IP geolocation&lt;/strong&gt;: Querying third-party databases that map IP addresses to approximate locations.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In the best case scenario, the browsers calls out to the Operating System&amp;#x27;s location services, which uses GPS to determine your location. But not all devices have GPS and not all operating systems provide that API. So even when the OS has location services, it still might be using third-party services. All browsers fall back to sending data to a location service provider in the absense of that OS API.&lt;/p&gt;&lt;p&gt;Chrome uses Google Location Services. Safari uses Apple&amp;#x27;s servers. Firefox had something called Mozilla Location Service but they retired it in 2024 and now also use Google&amp;#x27;s services.&lt;/p&gt;&lt;p&gt;This means the &amp;quot;web standard&amp;quot; geolocation depends on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Their integration and access to your device&amp;#x27;s OS location services&lt;/li&gt;&lt;li&gt;The browser vendor&amp;#x27;s relationship with location service providers&lt;/li&gt;&lt;li&gt;The quality and coverage of their Wi-Fi database&lt;/li&gt;&lt;li&gt;Their privacy policies around location data&lt;/li&gt;&lt;li&gt;Whether their servers are operational and accessible in your user&amp;#x27;s region&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;A PWA relying on precise geolocation might work flawlessly during Chrome testing but fail for users in regions where Google services are blocked.&lt;/p&gt;&lt;h2 id=&quot;speech-synthesis-shouting-from-clouds&quot;&gt;Speech Synthesis: Shouting from clouds&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API&quot;&gt;Web Speech API&lt;/a&gt; seems straightforward, and pretty cool:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; utterance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeechSynthesisUtterance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Hello, world!&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
utterance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;voice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; speechSynthesis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getVoices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
speechSynthesis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;speak&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;utterance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But where do those voices come from? It varies wildly:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Desktop browsers&lt;/strong&gt; often use operating system TTS engines (which themselves vary by OS and version).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Mobile browsers&lt;/strong&gt; might use online services for better quality voices.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Chrome&lt;/strong&gt; can use both local and cloud-based voices depending on the selected voice and network availability.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Safari&lt;/strong&gt; primarily relies on system voices but quality varies significantly across iOS and macOS versions.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Whatever voices your user has available depends on the browser, the operating system, and the device, and you&amp;#x27;re not guaranteed that the voice you selected in testing will be available for all your users.&lt;/p&gt;&lt;p&gt;Some &amp;quot;good&amp;quot; voices only work when you&amp;#x27;re online because they&amp;#x27;re processed on vendor servers: your text is sent to a server, processes there and the audio is sent back.&lt;/p&gt;&lt;p&gt;That should give you some pause about what kind of information you&amp;#x27;re sending to those servers. Are you reading aloud a user&amp;#x27;s private data or private messages? You might be sending explicitly private data to third-party servers without realizing it.&lt;/p&gt;&lt;p&gt;The API is standardized. The implementation is anything but.&lt;/p&gt;&lt;h2 id=&quot;speech-recognition-guess-where-your-voice-goes&quot;&gt;Speech Recognition: Guess where your voice goes&lt;/h2&gt;&lt;p&gt;The flip side of speech synthesis is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition&quot;&gt;speech recognition&lt;/a&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; recognition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeechRecognition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
recognition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;continuous &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
recognition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;interimResults &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

recognition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onresult&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; transcript &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transcript&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;You said:&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transcript&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

recognition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Until recently the speechRecognition API was not available without an internet connection (MDN has a note about this at the top of the page), and that&amp;#x27;s a clue. Speech recognition in Chrome is powered by Google&amp;#x27;s cloud speech services in many instances. Your users&amp;#x27; voice data is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Captured by the browser&lt;/li&gt;&lt;li&gt;Sent to Google&amp;#x27;s servers&lt;/li&gt;&lt;li&gt;Processed by Google&amp;#x27;s speech recognition models&lt;/li&gt;&lt;li&gt;Returned as text&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Safari uses Apple&amp;#x27;s speech recognition services. Edge might use Azure Cognitive Services. The API looks the same, but:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Accuracy varies&lt;/strong&gt; based on which vendor&amp;#x27;s models you&amp;#x27;re using.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Language support differs&lt;/strong&gt; dramatically between providers.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Privacy implications&lt;/strong&gt;: some vendors explicitly use your users&amp;#x27; voice data to improve their models.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Offline capability&lt;/strong&gt; only exists where vendors provide on-device models (basically iOS/macOS).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is streaming audio of your users&amp;#x27; voices to vendor servers in real-time. The &amp;quot;web API&amp;quot; is really just a JavaScript wrapper around vendor speech services.&lt;/p&gt;&lt;p&gt;In more recent versions of Chrome (139, release in August 2025), you can set a &lt;code class=&quot;language-text&quot;&gt;processLocally&lt;/code&gt; flag to specify it should use local processing instead of sending data to Google&amp;#x27;s servers:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; recognition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeechRecognition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
recognition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processLocally &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It does this by downloading a language pack to the user&amp;#x27;s device to use for processing. If the specified language pack is not available locally, you&amp;#x27;ll get an error.&lt;/p&gt;&lt;p&gt;So that&amp;#x27;s good, however note that it&amp;#x27;s not the default behavior, and it&amp;#x27;s only available in Chrome.&lt;/p&gt;&lt;h2 id=&quot;passkeys-a-web-api-built-on-the-browser-password-manager&quot;&gt;Passkeys: a web API built on the browser password manager&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API&quot;&gt;WebAuthn API&lt;/a&gt; promises passwordless authentication:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; credential &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;credentials&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;publicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;challenge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;rp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Example Corp&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;example.com&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;user@example.com&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;User Name&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;pubKeyCredParams&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;alg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;public-key&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;authenticatorSelection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;authenticatorAttachment&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;platform&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;requireResidentKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The WebAuthn API is a standard, but passkeys only work because browsers have invested heavily in their password management infrastructure. When you create a passkey:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Chrome stores it in Google Password Manager (synced via Google&amp;#x27;s cloud).&lt;/li&gt;&lt;li&gt;Safari stores it in iCloud Keychain (synced via Apple&amp;#x27;s infrastructure).&lt;/li&gt;&lt;li&gt;Edge stores it in Microsoft Account (synced via Microsoft&amp;#x27;s cloud).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Passkeys are not part of the web stack, they&amp;#x27;re part of each browsers password manager, and only work because browsers have hooked up the APIs to their own password managers. The specification defines the API surface, but the actual credential storage, synchronization, and recovery mechanisms are entirely vendor-specific.&lt;/p&gt;&lt;p&gt;You&amp;#x27;re not just using a web standard, you&amp;#x27;re integrating with your browsers password manager (and full authentication ecosystem).&lt;/p&gt;&lt;p&gt;WebAuthn being tied to that built-in password management means Polypane doesn&amp;#x27;t support passkeys natively. We could build a password manager, but Electron currently has no way of hooking the Passkey APIs into a password manager backend. The workaround here is to use a password manager extension with Passkeys support like 1Password.&lt;/p&gt;&lt;h2 id=&quot;payment-request-api&quot;&gt;Payment Request API&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Payment_Request_API&quot;&gt;Payment Request API&lt;/a&gt; looks like it standardizes online payments:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// set up a payment&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaymentRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;supportedMethods&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;https://google.com/pay&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;merchantId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;12345&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;Total&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;amount&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;USD&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;29.99&amp;#x27;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// show the payment UI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But which payment methods work depends entirely on browser vendor partnerships:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Chrome&lt;/strong&gt;: Deep integration with Google Pay&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Safari&lt;/strong&gt;: Apple Pay&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Edge&lt;/strong&gt;: Microsoft integrations, but often defers to system capabilities.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt;: no support.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;supportedMethods&lt;/code&gt; field looks like a standard way to declare payment options, but in practice:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You need separate integration contracts with each payment provider.&lt;/li&gt;&lt;li&gt;Browser support for payment methods is often region-specific.&lt;/li&gt;&lt;li&gt;The UX varies wildly: Safari shows a native Apple Pay sheet, Chrome shows its own UI.&lt;/li&gt;&lt;li&gt;Some browsers require additional user setup (like adding cards to Apple Wallet).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You&amp;#x27;re not building on a payment standard. You&amp;#x27;re building adapters for each browser&amp;#x27;s preferred payment partners. Which, for the big three browsers, happen to be... themselves. That&amp;#x27;s convenient.&lt;/p&gt;&lt;h2 id=&quot;web-push-three-different-push-networks&quot;&gt;Web Push: three different push networks&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Push_API&quot;&gt;Push API&lt;/a&gt; promises a standard way to send notifications:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; registration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;/sw.js&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pushManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;userVisibleOnly&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;applicationServerKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;urlBase64ToUint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But when you send that push notification, where does it actually go?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Chrome/Edge&lt;/strong&gt;: Firebase Cloud Messaging (FCM) - Google&amp;#x27;s infrastructure.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Safari&lt;/strong&gt;: Apple Push Notification service (APNs) - Apple&amp;#x27;s infrastructure.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt;: Mozilla Push Service - Mozilla&amp;#x27;s infrastructure (or FCM on Android).&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Each service has different:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Rate limits&lt;/strong&gt;: FCM allows 10,000 notifications per second per sender, but others have different limits&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Message size limits&lt;/strong&gt;: FCM allows 4KB, APNs allows 5KB for modern devices&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Delivery guarantees&lt;/strong&gt;: Each service handles offline delivery differently&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Privacy models&lt;/strong&gt;: Each service has different data retention and encryption practices&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Your push notification doesn&amp;#x27;t go directly to the user. Instead it routes through the browser vendor&amp;#x27;s servers first. If Google&amp;#x27;s FCM has an outage, Chrome push notifications stop working. If your server is in a region where Apple services are restricted, Safari push won&amp;#x27;t work for your users.&lt;/p&gt;&lt;p&gt;You&amp;#x27;re not just implementing push notifications. You&amp;#x27;re integrating with cloud infrastructure.&lt;/p&gt;&lt;h2 id=&quot;media-source-api-and-widevine-codec-politics-and-drm-dependencies&quot;&gt;Media Source API and Widevine: codec politics and DRM dependencies&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API&quot;&gt;MSE&lt;/a&gt; lets you build custom video players:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; video &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;video&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mediaSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MediaSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mediaSource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;sourceopen&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;revokeObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sourceBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mediaSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addSourceBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;video/mp4; codecs=&amp;quot;avc1.42E01E&amp;quot;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// fetch and append video segments here (not shown for brevity)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But which codecs are actually supported? That depends on:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Patent licensing&lt;/strong&gt;: Browsers ship codecs they&amp;#x27;re willing to pay for or that are royalty-free&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Platform support&lt;/strong&gt;: Some codecs only work if the OS provides hardware decoding&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Vendor strategy&lt;/strong&gt;: Google pushes VP9/AV1, Apple prefers H.264/HEVC, Mozilla champions open codecs&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Safari has limited VP9 support. Chrome&amp;#x27;s HEVC support depends on hardware availability. Your &amp;quot;standard&amp;quot; video playback depends on vendor decisions about codec licensing, hardware partnerships, and competitive strategy.&lt;/p&gt;&lt;p&gt;So the API is standard, but you&amp;#x27;re either going to use the lowest-common-denominator codec, or build a complex system that encodes videos in multiple formats and serves the right one to each browser.&lt;/p&gt;&lt;h3 id=&quot;the-widevine-problem&quot;&gt;The Widevine Problem&lt;/h3&gt;&lt;p&gt;Then there&amp;#x27;s DRM. The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API&quot;&gt;Encrypted Media Extensions (EME) API&lt;/a&gt; looks standard:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;initDataTypes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;cenc&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;videoCapabilities&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;video/mp4; codecs=&amp;quot;avc1.42E01E&amp;quot;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

navigator
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestMediaKeySystemAccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;com.widevine.alpha&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; access&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&amp;gt;&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMediaKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mediaKeys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// start a video playback session here (not shown for brevity)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But Widevine isn&amp;#x27;t a web standard, it&amp;#x27;s Google&amp;#x27;s proprietary DRM system. And it&amp;#x27;s not universally available:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Chrome, Edge, Firefox&lt;/strong&gt;: Include Widevine by default (through licensing agreements with Google).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Safari&lt;/strong&gt;: Uses Apple&amp;#x27;s FairPlay DRM instead, requiring a completely different integration.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Smaller browsers&lt;/strong&gt;: Often don&amp;#x27;t have Widevine at all due to licensing costs, technical, legal and business requirements. The licensing barriers are often prohibitive.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Polypane&lt;/strong&gt;: Supports Widevine (mostly), but we too are at the mercy of whether a Widevine module is available for the OS and hardware you use, as well as it being available in the version of Electron we use.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Streaming services like Netflix, Disney+, and Spotify require not just codec support, but specific DRM and won&amp;#x27;t work without it.&lt;/p&gt;&lt;p&gt;The way this is currently set up strongly disadvantages smaller browsers and enforces vendor lock-in.&lt;/p&gt;&lt;h2 id=&quot;ai-apis&quot;&gt;AI APIs&lt;/h2&gt;&lt;p&gt;Recently, browsers (Chrome) have started exposing &amp;quot;AI&amp;quot; capabilities directly through web APIs. Google has a number of these AI-backed APIs in development, like summarization, translation, prompting, writing, and proofreading. They have some impressive demos, and clever people working on them.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Summarization&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; summarizer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Summarizer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;key-points&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;expectedInputLanguages&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;outputLanguage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; longText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;article&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; summary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; summarizer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;summarize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;longText&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;This article is intended for front-end developers.&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Translation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; translator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Translator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;sourceLanguage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;en&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;targetLanguage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;es&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; translated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; translator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;#x27;Hello world&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above APIs are not &amp;#x27;web standards&amp;#x27; like the previous examples: they&amp;#x27;re not standardized and they&amp;#x27;re not available in other browsers. They&amp;#x27;re an experimental proposal by the Chrome team with the intent of standardizing them later.&lt;/p&gt;&lt;p&gt;The AI features in Chrome are only available in Chrome. They rely on a &amp;quot;small language model&amp;quot; called Gemini Nano, that is downloaded to your device when you first use it (...provided you have enough disk space and a good enough GPU). The model then runs locally on your device.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s pretty cool, because it means you&amp;#x27;re not sending your user&amp;#x27;s data to a server where it can be logged, stored, used for training or sold to third parties. Your users&amp;#x27; data stays on their device.&lt;/p&gt;&lt;p&gt;Unfortunately, &amp;quot;small&amp;quot; is only in comparison to the terabyte-scale models that power services like ChatGPT or Claude. Gemini Nano is still 4GB (varies per language) that needs to be downloaded by your user and requires significant compute resources to run effectively. It&amp;#x27;s also fully proprietary. Only Google gets to use it.&lt;/p&gt;&lt;p&gt;A small model running on a users device means that the same API can be used with a different model in a different browser. Theoretically that&amp;#x27;s cool.&lt;/p&gt;&lt;p&gt;However, while Microsoft will have no problem to train their own small Copilot model, smaller companies simply don&amp;#x27;t have the same resources to scrape, steal, build, train and maintain LLM models and pay for the infrastructure to host them and each of their users to download them.&lt;/p&gt;&lt;p&gt;That&amp;#x27;s simply something smaller browsers and open source projects can&amp;#x27;t compete with.&lt;/p&gt;&lt;h2 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h2&gt;&lt;p&gt;These APIs blur the line between web standards and browser features in ways that have real consequences:&lt;/p&gt;&lt;h3 id=&quot;portability-illusions&quot;&gt;Portability Illusions&lt;/h3&gt;&lt;p&gt;Code that works perfectly in Chrome might fail silently in Safari, not because of a bug or because you failed to check for availability, but because the underlying service infrastructure is different. Your geolocation might be accurate in one browser and wildly off in another, even on the same device for the same user.&lt;/p&gt;&lt;h3 id=&quot;privacy-surprises&quot;&gt;Privacy Surprises&lt;/h3&gt;&lt;p&gt;Your users might not realise that the feature you offer is sending their data to vendor servers. The Speech Recognition API feels local, but you&amp;#x27;re streaming audio to Google or Apple. Geolocation phones home to different companies depending on the browser. Web Push routes through vendor networks.&lt;/p&gt;&lt;h3 id=&quot;web-standards-become-a-way-for-big-players-to-keep-out-smaller-competitors&quot;&gt;Web standards become a way for big players to keep out smaller competitors&lt;/h3&gt;&lt;p&gt;The web is a level playing field. The web is open. The web is for everyone. It&amp;#x27;s an idea we want to be true.&lt;/p&gt;&lt;p&gt;But with the above APIs, the web is not a level playing field. Big players have the resources to build and maintain the services these APIs depend on. They can afford to offer them for free to their users because they have other ways of monetizing them (ads, ecosystem lock-in, data collection).&lt;/p&gt;&lt;p&gt;Standardization should guard against this kind of vendor lock-in. But instead, web specifications are mostly written by the people working for these big players because these companies can afford to employ people to work on specs full-time and employ people to implement the specifications. And when there&amp;#x27;s implementations, specifications become standards.&lt;/p&gt;&lt;p&gt;The W3C has guards to prevent abuse, but in practice the process is weighed towards the wishes of large companies. The specification process is a way for the big players to entrench their dominance.&lt;/p&gt;&lt;p&gt;Smaller browsers and open source projects either need to rent access to the services that Google, Apple and Microsoft can afford to offer for free or deal with explaining to users why their browser can&amp;#x27;t offer the same features. This creates feature parity gaps that look as if they are browser bugs to end users.&lt;/p&gt;&lt;h3 id=&quot;what-this-means-for-developers&quot;&gt;What this means for developers&lt;/h3&gt;&lt;p&gt;The above APIs are really useful. I&amp;#x27;m not saying browsers are bad for having more capabilities. These are features we &lt;em&gt;want&lt;/em&gt; to offer to our users, and having them &amp;quot;build right into the browser&amp;quot; feels great.&lt;/p&gt;&lt;p&gt;But we need to understand that these APIs are not truly portable web standards, and understand the consequences of using them for our users.&lt;/p&gt;&lt;p&gt;They&amp;#x27;re abstractions over vendor services, and we should treat them as such. We sign data processing agreements with all our vendors to keep our users data safe. Why does the browser get a pass?&lt;/p&gt;&lt;h2 id=&quot;what-should-we-do&quot;&gt;What Should We Do?&lt;/h2&gt;&lt;p&gt;As developers, we need to be aware of these realities: the APIs you choose to use come with consequences.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Treat these APIs as abstractions over vendor services&lt;/strong&gt;, not as truly portable web standards. Document which browsers you&amp;#x27;ve tested with and what the fallback behavior is. Consider feature detection not just for API availability, but for quality and behavior.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Design for graceful degradation&lt;/strong&gt;. Don&amp;#x27;t assume geolocation will be accurate, passkeys will sync, speech will sound the same everywhere, or push notifications will be delivered reliably. Build fallbacks.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Be transparent about privacy&lt;/strong&gt;. If an API might send data to third-party servers, your users deserve to know. Especially if the browser abstracts it away. Document which vendor services your app depends on.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Consider the vendor relationship&lt;/strong&gt;. When you use these APIs, you&amp;#x27;re implicitly depending on each browser vendor&amp;#x27;s ongoing commitment to maintaining their backend infrastructure. What happens if a vendor decides to deprecate a service or changes their pricing model?&lt;/p&gt;&lt;p&gt;This isn&amp;#x27;t just theoretical: There are vast swaths of web pages with broken Google Maps embeds because Google &lt;em&gt;changed the deal&lt;/em&gt; and started requiring API keys (with usage limits and billing) for them.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Test across browsers and regions&lt;/strong&gt;. That speech recognition that&amp;#x27;s accurate for you might not support your users&amp;#x27; languages.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Plan for vendor lock-in&lt;/strong&gt;. If you build heavily on one of these features, you&amp;#x27;re making a strategic choice to depend on that vendor&amp;#x27;s platform. That might be fine, but make it a conscious decision.&lt;/p&gt;&lt;h2 id=&quot;the-web-we-think-we-have-vs-the-web-we-actually-have&quot;&gt;The web we think we have vs. The web we actually have&lt;/h2&gt;&lt;p&gt;The promise of web standards is that you write code once and it works everywhere. But the above APIs are really thin wrappers around vendor-specific services.&lt;/p&gt;&lt;p&gt;The interface is standardized, but the implementation, its dependencies, limitations, and privacy implications are not.&lt;/p&gt;&lt;p&gt;When you call &lt;code class=&quot;language-text&quot;&gt;navigator.geolocation.getCurrentPosition()&lt;/code&gt;, you&amp;#x27;re not just using a web API. You&amp;#x27;re using Google Location Services or Apple&amp;#x27;s location servers. When you send a push notification, you&amp;#x27;re routing through FCM or APNs or Mozilla&amp;#x27;s infrastructure. When you use speech recognition, you&amp;#x27;re streaming audio to vendor cloud services.&lt;/p&gt;&lt;p&gt;The specification says what the function call looks like. But specifications don&amp;#x27;t tell you:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Where your data is sent to,&lt;/li&gt;&lt;li&gt;How accurate or reliable the results will be,&lt;/li&gt;&lt;li&gt;Whether it works in your users&amp;#x27; regions,&lt;/li&gt;&lt;li&gt;What the privacy implications are,&lt;/li&gt;&lt;li&gt;Whether it will still work for free (or at all) next year.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This doesn&amp;#x27;t make these APIs bad or unusable. It just means we need to design our applications with a clearer understanding of where the standardization ends and the vendor specifics begin.&lt;/p&gt;&lt;p&gt;The web platform is powerful, but it&amp;#x27;s also more fractured than we often acknowledge. And that&amp;#x27;s worth keeping in mind every time we reach for an API that looks standard but depends on infrastructure that isn&amp;#x27;t.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Polypane 27.1: Expanding Portal]]></title><description><![CDATA[Polypane 27.1 is here and Polypane Portal has expanded! The Polypane console will now show the console messages from all connected browsers…]]></description><link>https://polypane.app/blog/polypane-27-1-expanding-portal/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-27-1-expanding-portal/</guid><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 27.1 is here and Polypane Portal has expanded! The Polypane console will now show the console messages from all connected browsers so you get instant feedback on errors and your own logs. Snippets are now also injected and ran in connected portals. This combination makes Polypane&amp;#x27;s Portal an even more powerful tool for testing and debugging across multiple browser and real devices. The elements panel and projects have also received a lot of quality of life improvements in this release.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;portal&quot;&gt;Portal&lt;/h2&gt;&lt;p&gt;Polypane &lt;a href=&quot;/portal/&quot;&gt;Portal&lt;/a&gt; is a powerful way to test and debug your site across browsers and devices by making your localhost available to the entire network or even the entire internet with zero setup. Once connected, browsers or devices are kept in sync with Polypane so as you scroll, type, inspect and edit in Polypane, your actions are mirrored in all connected browsers. It&amp;#x27;s pretty awesome.&lt;/p&gt;&lt;h3 id=&quot;console-messages-in-portal&quot;&gt;Console messages in Portal&lt;/h3&gt;&lt;p&gt;In 27.1 all connected browsers now send their console messages back to Polypane where they&amp;#x27;re shown in the &lt;a href=&quot;/docs/console/&quot;&gt;Polypane Console&lt;/a&gt;. This means you can now see errors and logs from all connected browsers without needing to open devtools on each device or using other convoluted methods.&lt;/p&gt;&lt;video src=&quot;static/console-12823363837572176c1b91425c1db14c.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;Code inputted in the Polypane console is also ran in all connected browsers, so inspecting and debugging is now easier than ever.&lt;/p&gt;&lt;h3 id=&quot;snippets-in-portal&quot;&gt;Snippets in Portal&lt;/h3&gt;&lt;p&gt;When Portal is connected you can opt to run &lt;a href=&quot;/docs/snippets/&quot;&gt;snippets&lt;/a&gt; in all connected browsers. Whether you have specific CSS debugging snippets, JS utilities you want to have available on page load or specific JS functions you run through the Snippets panel, all of those can now be ran in your Portal connected browsers too.&lt;/p&gt;&lt;video src=&quot;static/snippets-ab555448607026433bc66e3966608708.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;h2 id=&quot;elements-panel&quot;&gt;Elements panel&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/elements-panel/&quot;&gt;Elements panel&lt;/a&gt; has received quite a few improvements in this release to make it easier to use and more powerful.&lt;/p&gt;&lt;h3 id=&quot;arrow-key-navigation&quot;&gt;Arrow key navigation&lt;/h3&gt;&lt;p&gt;When selecting an element in the DOM tree, you can now use the arrow keys to navigate around the tree:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Up&lt;/kbd&gt;/&lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Down&lt;/kbd&gt; arrows move to the previous/next line.&lt;/li&gt;&lt;li&gt;&lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Left&lt;/kbd&gt; arrow collapses the current node or moves to the parent if already collapsed.&lt;/li&gt;&lt;li&gt;&lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Right&lt;/kbd&gt; arrow expands the current node or moves to the first child if already expanded.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;This is a really quick way to navigate deeply nested trees without needing to click around a lot, and an easy way to quickly move back up the DOM.&lt;/p&gt;&lt;p&gt;When the DOM tree has focus, you can also press &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;h&lt;/kbd&gt; to hide the currently selected element.&lt;/p&gt;&lt;h3 id=&quot;constructed-stylesheet-editing&quot;&gt;Constructed stylesheet editing&lt;/h3&gt;&lt;p&gt;You can now edit constructed stylesheets in the elements panel. These are stylesheets that are added to a document or web component root through JavaScript using the &lt;code class=&quot;language-text&quot;&gt;adoptedStyleSheets&lt;/code&gt; API.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Styles coming from a constructed stylesheet.&quot;/&gt;&lt;p&gt;Polypane had support for this in the Elements panel before, but the API for constructed stylesheets has changed (they&amp;#x27;re no longer available on the &lt;code class=&quot;language-text&quot;&gt;shadowRoot&lt;/code&gt;) and the implementation had to be updated. You can now again view and edit styles set through constructed stylesheets.&lt;/p&gt;&lt;h3 id=&quot;collapsing-unused-custom-properties&quot;&gt;Collapsing unused custom properties&lt;/h3&gt;&lt;p&gt;A common pattern has emerged where all custom properties are defined on &lt;code class=&quot;language-text&quot;&gt;:root&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; to make them available throughout the app. Because of the way the elements panel collects styles, this means that each inspected element had dozens, if not hundreds of custom properties to be rendered in the elements panel.&lt;/p&gt;&lt;p&gt;Because these custom properties often have a color widget, rendering them is relatively expensive and this had a performance impact on the elements panel.&lt;/p&gt;&lt;img src=&quot;static/unused-css-a8e2934ee66cb4df3a353fa4e2574f3a.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;A badge in the style panel reading &amp;#x27;show 109 unused custom CSS properties&amp;#x27;.&quot;/&gt;&lt;p&gt;To improve performance, Polypane now initially only renders the custom properties that are used by the selected element and hides the rest behind a &amp;quot;Show unused custom properties&amp;quot; button. Clicking that button will expand all custom properties so you can inspect and edit them as needed. All other places where Polypane shows custom properties, like in the value suggestions, will continue to list all of them for easy access.&lt;/p&gt;&lt;p&gt;This performance improvement was recently &lt;a href=&quot;https://bsky.app/profile/webdevs.firefox.com/post/3m7rst5tscw27&quot;&gt;added by the Firefox&amp;#x27; DevTools team&lt;/a&gt; and it was such a good idea that I decided to implement it in Polypane as well.&lt;/p&gt;&lt;h3 id=&quot;filtering-computed-styles&quot;&gt;Filtering computed styles&lt;/h3&gt;&lt;p&gt;The computed styles overview now has a filter input at the top so you can quickly find the property you&amp;#x27;re looking for without needing to scroll through the entire list. Filter on both property names and values.&lt;/p&gt;&lt;img src=&quot;static/computedfilter-7e36bca9ab98692c6aa12ef29e6992e1.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Computed styles filtered down to things that just say color&quot;/&gt;&lt;h3 id=&quot;hold-cmdctrl-to-hide-element-info&quot;&gt;Hold cmd/ctrl to hide element info&lt;/h3&gt;&lt;p&gt;When hovering elements in the DOM tree, Polypane shows a &lt;a href=&quot;/docs/polypane-peek/&quot;&gt;tooltip with information&lt;/a&gt; about the element like its dimensions, font, contrast, accessible name and role.&lt;/p&gt;&lt;img src=&quot;static/cmdtooltip-ad123ecffbeccbc7165f8671c77de2da.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Highlighted element without info popup&quot;/&gt;&lt;p&gt;You can now hold the &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⌘&lt;/kbd&gt; key while hovering to show the margin, padding etc while hide the info panel. so nothing is obscuring parts of the page.&lt;/p&gt;&lt;h2 id=&quot;projects&quot;&gt;Projects&lt;/h2&gt;&lt;p&gt;I&amp;#x27;m overwhelmed by the positive response to Polypane Projects! It&amp;#x27;s great to see so many people using this powerful way to manage their work in Polypane, and I immediately went to work to make Projects even better based on your feedback.&lt;/p&gt;&lt;h3 id=&quot;bookmarks-filtering-and-navigation&quot;&gt;Bookmarks filtering and navigation&lt;/h3&gt;&lt;p&gt;Opening the bookmarks panel now focuses a filter input so you can quickly find the bookmark you&amp;#x27;re looking for. As you type, the bookmarks list is filtered to only show matching bookmarks.&lt;/p&gt;&lt;img src=&quot;static/bookmarkfilter-7c69d3caea0659e0b9b038c54ce36362.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;A badge in the style panel reading &amp;#x27;show 109 unused custom CSS properties&amp;#x27;.&quot;/&gt;&lt;p&gt;You can also now navigate the bookmarks list using the &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Up&lt;/kbd&gt; and &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Down&lt;/kbd&gt; arrow keys to move through the list and &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Enter&lt;/kbd&gt; to open the selected bookmark in a new tab.&lt;/p&gt;&lt;h3 id=&quot;importexport-projects-and-bookmarks&quot;&gt;Import/export projects and bookmarks&lt;/h3&gt;&lt;p&gt;A new import and export system for Projects and Bookmarks makes it easier to back up you work, share projects with colleagues or move projects between different Polypane installations.&lt;/p&gt;&lt;img src=&quot;static/projectsettings-201840870f7ed74af05355d9f39d75cb.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;New export and import buttons in the project settings.&quot;/&gt;&lt;h4 id=&quot;projects-importexport&quot;&gt;Projects import/export&lt;/h4&gt;&lt;p&gt;Projects with all their settings can now be exported to JSON files. These files can then be imported into other Polypane installations to recreate the exact same project structure, settings and bookmarks.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ll be sharing out the JSON Schema for this soon, so third party tools can also generate Polypane projects for you. Imagine configuring your accessibility audit in an online tool, then exporting it and having all the pages that are part of your audit automatically set up in Polypane!&lt;/p&gt;&lt;h4 id=&quot;bookmarks-importexport&quot;&gt;Bookmarks import/export&lt;/h4&gt;&lt;p&gt;Separately Bookmarks are now also importable and exportable. You can export all bookmarks in a project to a JSON file which can then be imported into other Polypane installations.&lt;/p&gt;&lt;p&gt;Additionally, Polypane now supports importing bookmarks from a &lt;code class=&quot;language-text&quot;&gt;bookmarks.html&lt;/code&gt;, a weird and old format that all browsers support.&lt;/p&gt;&lt;p&gt;This makes it easy to migrate your bookmarks from any browser to Polypane. When importing a bookmarks.html file all non-URL entries (like bookmarklets or browser internals) are filtered out automatically.&lt;/p&gt;&lt;h3 id=&quot;hide-project-selector-button&quot;&gt;Hide project selector button&lt;/h3&gt;&lt;p&gt;If you do not need the projects feature, you can now hide the project selector button by going to the &lt;a href=&quot;&quot;&gt;settings&lt;/a&gt; in the address bar and unchecking &amp;quot;Show project selector&amp;quot;. Thanks Artem for the suggestion!&lt;/p&gt;&lt;img src=&quot;static/showprojectselector-9929e6dda58fc5a20b7f78896fbfe50d.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Project settings with &amp;#x27;Show project selector&amp;#x27; option highlighted.&quot;/&gt;&lt;h2 id=&quot;snippets-panel-improvements&quot;&gt;Snippets panel improvements&lt;/h2&gt;&lt;p&gt;Snippets are now also available in portal, but the snippets panel itself has also received two quality of life improvements.&lt;/p&gt;&lt;h3 id=&quot;importing-bookmarkshtml&quot;&gt;Importing Bookmarks.html&lt;/h3&gt;&lt;p&gt;You can now import snippets from a bookmarks.html file. The import automatically filters down to the bookmarklets, and adds them as snippets. If your bookmarks has folders, those are also recreated and the snippets are automatically placed in the folder they were in in the bookmarks.html file.&lt;/p&gt;&lt;h4 id=&quot;deleting-folders&quot;&gt;Deleting folders&lt;/h4&gt;&lt;p&gt;When deleting a folder in the snippets panel, all contained snippets are now also deleted. Previously Polypane would just move the snippets to the root folder, but this created a lot of additional work when you wanted to delete a whole folder of snippets. Now you can just delete the folder and all its contents in one go.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Delete confirmation dialog for deleting a folder with 2 snippets.&quot;/&gt;&lt;p&gt;Any snippet you want to keep can be moved out of the folder before deleting it.&lt;/p&gt;&lt;h2 id=&quot;outline-panel&quot;&gt;Outline panel&lt;/h2&gt;&lt;p&gt;The Outline panel is one of the most used features in Polypane, but heavy pages could feel slow in Polypane as it indexed, analysed and rendered sometimes thousands of links, headings and images parsed from the page, and doing so after every DOM change on the page.&lt;/p&gt;&lt;p&gt;To prevent this performance impact, the outline panel for new tabs now always starts in a paused state. From there you have two options:&lt;/p&gt;&lt;img src=&quot;static/pausedtracking-64b31a04e5769d9fe09130f93d8cabaf.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Outline panel with tracking paused.&quot;/&gt;&lt;ul&gt;&lt;li&gt;Get the outline &lt;em&gt;once&lt;/em&gt; by clicking the load button, which fetches the data once and then pauses again.&lt;/li&gt;&lt;li&gt;Enable automatic tracking by clicking the Track button: As the DOM changes, for example through user interaction or scripts running on the page, the outline will update automatically so you&amp;#x27;re always seeing the actual outline of the page.&lt;/li&gt;&lt;/ul&gt;&lt;img src=&quot;data:image/png;base64,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&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Outline panel with tracking active.&quot;/&gt;&lt;p&gt;This way, you can choose when you want the outline to update automatically, when you want to just get the outline once without impacting performance, and when you don&amp;#x27;t need the outline at all.&lt;/p&gt;&lt;h3 id=&quot;outline-rendering-improvements&quot;&gt;Outline rendering improvements&lt;/h3&gt;&lt;p&gt;There are also made a number of improvements to the outline rendering performance so that even when automatic updating is enabled, the outline panel is more responsive than before, especially on large outlines.&lt;/p&gt;&lt;h3 id=&quot;image-outline-roleimg-support&quot;&gt;Image outline role=img support&lt;/h3&gt;&lt;p&gt;The image outline now also includes elements with the &lt;code class=&quot;language-text&quot;&gt;role=&amp;quot;img&amp;quot;&lt;/code&gt; attribute. This is especially useful for SVG images that use this role to provide accessible names and descriptions.&lt;/p&gt;&lt;img src=&quot;static/roleimg-d5abbc2390bb1f0d2f9586e08051bb98.png&quot; class=&quot;imgshadow&quot; loading=&quot;async&quot; alt=&quot;Outline panel showing an element with role img.&quot;/&gt;&lt;p&gt;It took a while to find the right solution for this, as elements with &lt;code class=&quot;language-text&quot;&gt;role=&amp;quot;img&amp;quot;&lt;/code&gt; are not transportable like regular image files. They might simply be a HTML div, or SVGs might have external CSS or even import a specific group from an external SVG file.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;role=&amp;quot;img&amp;quot;&lt;/code&gt; elements are shows as a placeholder in the aspect ratio of the element. The outline will show you all the important information like the accessible name and dimensions so you can verify that these images are accessible. Thanks Robbert for the suggestion!&lt;/p&gt;&lt;h3 id=&quot;focus-outline-accessible-name-warnings&quot;&gt;Focus outline accessible name warnings&lt;/h3&gt;&lt;p&gt;The focus outline falls back to the text content of an element if it has no accessible name to make it easier to see what element &lt;em&gt;has focus&lt;/em&gt;. This is a deliberate choice, but it also can make it seem like an element has an accessible name when it actually doesn&amp;#x27;t.&lt;/p&gt;&lt;p&gt;The Focus outline will still fall back to the text content to aid the visual identification of focused elements, but now also shows a warning next to elements that have no accessible name so you can easily identify these elements and fix them. Other outlines and the Polypane Peek tooltip have always shown these warnings, so this makes the focus outline consistent with the rest of Polypane. Thanks Ron for the suggestion!&lt;/p&gt;&lt;h2 id=&quot;meta-panel&quot;&gt;Meta panel&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/meta-information/&quot;&gt;meta panel&lt;/a&gt; now uses the newly released Capo.js 2.0.0 for its head order overview. Capo 2 is smaller and faster.&lt;/p&gt;&lt;h2 id=&quot;markerio-integration-update&quot;&gt;Marker.io integration update&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/integrations/markerio/&quot;&gt;Marker.io integration&lt;/a&gt; needed an API update to keep working. The Marker button will now appear again when set up.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-271&quot;&gt;Get Polypane 27.1&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14 day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-271-changelog&quot;&gt;Polypane 27.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Portal: Console messages from connected browsers are now shown in the main Polypane console&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Portal: Snippets are now ran in connected browsers&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Arrow key navigation for DOM tree&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Support for adopted stylesheet editing&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Filtering for computed styles&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: collapsing of unused custom properties&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Outline panel: Enable/disable automatic updating&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects: Import/export projects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects: Import/export bookmarks&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects: Import bookmarks.html file support&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects: Filter bookmarks and navigate by keyboard&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Support for referential IDREFs&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Hold cmd/ctrl while hovering DOM tree to hide element info&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: use &lt;code class=&quot;language-text&quot;&gt;keys()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;values()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;entries()&lt;/code&gt; to log iterable objects&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Console panel: messages now support multiple styles using %c&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Show elements with role=img in image outline (Thanks Robbert!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Improve rendering performance for large outlines&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Focus outline now shows warning for missing accessible names (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessibility info updates fasted across the app&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Color picker: Picking a new selected color no longer changes the background color (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Projects: option to hide project selector button (Thanks Artem!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Projects: Adding bookmarks will refocus the URL input&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Quick start: Links are now opened in new tabs (Thanks Max!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Capo.js head order overview updated to 2.0.0&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippets: Deleting a folder now deletes all containing snippets&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippets: Support importing bookmarks HTML&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippets: Updated list of Google fonts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated Chromium to 142.0.7444.226&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Marker.io integration needed an API update.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; QR for Portal now updates correctly (Thanks Jesper!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Console popups would sometimes lock up the app (Thanks Luan!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Projects: Add bookmarks as new tabs would not work (Thanks Andy!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Split view: Prevent rulers from overlapping pane headers&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: CSS Suggestions for new properties would be clipped&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Fix HTML editing styling on Linux&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: adding styles with trailing ; works automatically&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Sidebar: Issue where sidebar could be visible without it being opened&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Polypane 27: Projects, a Snippets Panel and Chromium 142]]></title><description><![CDATA[Polypane 27 is out! This release brings a new project-focused workflow, a new snippets panel, improved DOM editing and is built on Chromium…]]></description><link>https://polypane.app/blog/polypane-27-projects-a-snippets-panel-and-chromium-142/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-27-projects-a-snippets-panel-and-chromium-142/</guid><pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 27 is out! This release brings a new project-focused workflow, a new snippets panel, improved DOM editing and is built on Chromium 142. Here&amp;#x27;s everything that&amp;#x27;s new in Polypane 27.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;polypane-projects&quot;&gt;Polypane Projects&lt;/h2&gt;&lt;p&gt;A project-based workflow has been one of the most requested features for Polypane, and it took me a while to find the right approach. Starting with Polypane 27, Projects are now a core part of Polypane.&lt;/p&gt;&lt;p&gt;A new button in the top left of the app shows you the project you&amp;#x27;re currently working on, and clicking it opens the project overview.&lt;/p&gt;&lt;img src=&quot;static/selector-c4ae1c6ebe5adabb3044857ec97f68e3.png&quot; class=&quot;imgshadow&quot; alt=&quot;Project Selector&quot;/&gt;&lt;p&gt;Your projects control the entire browser. Create a project for each of your websites or clients and switch between them easily. All tabs, bookmarks, sessions and other settings are stored per project so you never have to spend time re-configuring Polypane when switching between different tasks.&lt;/p&gt;&lt;p&gt;Did you notice that said bookmarks? Yes, Polypane has bookmarks now! All the relevant pages and URLs can be added to your Project bookmarks and quickly opened from the new Bookmarks button, in regular tabs or in the Browse panel.&lt;/p&gt;&lt;img src=&quot;static/bookmarks-33c1d1e4a9c3cdf7a5d28826ed1dff4c.png&quot; class=&quot;imgshadow&quot; alt=&quot;Bookmarks dropdown&quot;/&gt;&lt;p&gt;Projects is set up in a way that&amp;#x27;s very easy to get started with (you can even ignore it if you want, or only use the bookmarks part) and very powerful for everyone that works on multiple projects.&lt;/p&gt;&lt;p&gt;Check out the &lt;a href=&quot;/docs/projects/&quot;&gt;Projects documentation&lt;/a&gt; to learn more.&lt;/p&gt;&lt;h2 id=&quot;snippets-panel&quot;&gt;Snippets Panel&lt;/h2&gt;&lt;p&gt;Over five years ago &lt;strong&gt;Polypane 2.1 (!)&lt;/strong&gt; introduced the Live CSS panel, which let you add CSS that would be applied to all panes in Polypane. While it was a great way to quickly test CSS changes, the Elements panel has caught up and exceeded it in functionality.&lt;/p&gt;&lt;p&gt;In 27, the Live CSS panel has been rebuilt into the new &lt;a href=&quot;/docs/snippets/&quot;&gt;Snippets panel&lt;/a&gt;. The Snippets panel lets you add both CSS and JavaScript snippets that can be saved, edited and run on any page you visit.&lt;/p&gt;&lt;img src=&quot;static/snippets-e911470bfce63515882f9d71247d6b6e.png&quot; class=&quot;imgshadow&quot; alt=&quot;Project Selector&quot;/&gt;&lt;p&gt;It&amp;#x27;s filled with clever features that make it a joy to use, like code completion for CSS and JS, smart bookmarklet support and grouping of snippets to quickly toggle an entire set of snippets on and off.&lt;/p&gt;&lt;p&gt;There&amp;#x27;s also a robust importing/exporting system that lets you share snippets between different Polypane installations and across your team, or import bookmarklets from other browsers. Expect more snippet sharing in the future!&lt;/p&gt;&lt;h2 id=&quot;new-macos-icon&quot;&gt;New macOS Icon&lt;/h2&gt;&lt;p&gt;Polypane 27 ships with a new macOS icon that uses the new Liquid Glass style, with special styles for light, dark and tinted mode.&lt;/p&gt;&lt;video src=&quot;static/liquidglassicon-cfca255776ddc7e835bb68daa1529270.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;h2 id=&quot;elements-panel-updates&quot;&gt;Elements panel updates&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/elements-panel/&quot;&gt;Elements Panel&lt;/a&gt; has received several improvements in Polypane 27 to make it easier to use and more powerful.&lt;/p&gt;&lt;h3 id=&quot;line-numbers-for-css-styles&quot;&gt;Line numbers for CSS Styles&lt;/h3&gt;&lt;p&gt;The CSS Styles section in the Elements panel now shows line numbers for each CSS rule, making it easier to identify where styles are coming from.&lt;/p&gt;&lt;img src=&quot;static/linenumbers-7735d0534ba4c108838169f6f1808173.png&quot; class=&quot;imgshadow&quot; alt=&quot;Line numbers in CSS Styles&quot;/&gt;&lt;p&gt;An often requested feature that was really complex to implement, but our line number detection is on par with other major browser devtools now.&lt;/p&gt;&lt;h3 id=&quot;force-starting-style&quot;&gt;Force @starting-style&lt;/h3&gt;&lt;p&gt;The Elements panel now has a &amp;quot;Force starting style&amp;quot; feature in the toggle element states dropdown that lets you see the styles that would be applied to an element when it&amp;#x27;s added to the dom.&lt;/p&gt;&lt;img src=&quot;static/forcestartingstyle-45171267bf017a7f53102d8666b99271.png&quot; class=&quot;imgshadow&quot; alt=&quot;Force starting style feature&quot;/&gt;&lt;p&gt;When turned on, the starting styles will be applied and the element will get a special badge in the tree view to indicate that it&amp;#x27;s using the starting styles.&lt;/p&gt;&lt;p&gt;This build ons on the support for editing &lt;code class=&quot;language-text&quot;&gt;@starting-style&lt;/code&gt; (also visible in the screenshot) that we added in &lt;a href=&quot;/blog/polypane-20-browser-features-and-performance/#starting-style-support&quot;&gt;Polypane 20&lt;/a&gt; back in June of 2024.&lt;/p&gt;&lt;h3 id=&quot;updated-hide-element-shortcut-on-macos&quot;&gt;Updated hide element shortcut (on macOS)&lt;/h3&gt;&lt;p&gt;In &lt;a href=&quot;/blog/polypane-26-accurate-device-emulation-with-safe-area-and-small-viewport-units/&quot;&gt;Polypane 26&lt;/a&gt; we added a &amp;quot;Hide element&amp;quot; shortcut to quickly hide the selected element in the elements panel. On macOS this was set to use CMD + H, but this conflicted with the system-wide shortcut that hides the current application.&lt;/p&gt;&lt;p&gt;In Polypane 27, we&amp;#x27;ve changed the shortcut to &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Ctrl h&lt;/kbd&gt; to prevent this conflict. On Windows and Linux, this was already set to &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Ctrl h&lt;/kbd&gt; and remains unchanged.&lt;/p&gt;&lt;h3 id=&quot;duplicate-and-remove-elements&quot;&gt;Duplicate and remove elements&lt;/h3&gt;&lt;p&gt;You can now right-click any element in the tree view to duplicate or remove it from the DOM. This makes it easy to quickly test changes to the DOM structure without needing to write code.&lt;/p&gt;&lt;p&gt;Unlike other browser devtools, right-clicking an element in the tree view won&amp;#x27;t also select that element so you never lose context when working with the Elements panel.&lt;/p&gt;&lt;h3 id=&quot;other-context-menu-options&quot;&gt;Other context menu options&lt;/h3&gt;&lt;p&gt;The new context menu on the elements now also quickly lets you select, hide or copy the element (as innerHTML, outerHTML, CSS selector or XPath) without needing to select the element first.&lt;/p&gt;&lt;h2 id=&quot;meta-panel&quot;&gt;Meta panel&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve split out &lt;code class=&quot;language-text&quot;&gt;alternate&lt;/code&gt; links in the &lt;a href=&quot;/docs/meta-information/&quot;&gt;Meta panel&lt;/a&gt; into their own section separately from other link elements. &lt;code class=&quot;language-text&quot;&gt;alternate&lt;/code&gt; links can be used for things like RSS feeds, alternate language versions of a page and other resources related to the current page. Thanks Charlie for the suggestion!&lt;/p&gt;&lt;img src=&quot;static/alternates-ddd8955eb31c6a428e2ce95cababf61a.png&quot; class=&quot;imgshadow&quot; alt=&quot;Alternates in Meta panel&quot;/&gt;&lt;p&gt;&lt;em&gt;The alternate links for apple.com. Note: truncated&lt;/em&gt;&lt;/p&gt;&lt;p&gt;As you&amp;#x27;re used to from Polypane, we&amp;#x27;ll automatically check for duplicates and issues with your alternate links and show warnings when something is amiss.&lt;/p&gt;&lt;p&gt;Slack ditched their small-image design for link previews recently, so we&amp;#x27;ve updated the Meta panel design to match their new look as well.&lt;/p&gt;&lt;h2 id=&quot;new-debug-tools&quot;&gt;New debug tools&lt;/h2&gt;&lt;p&gt;We&amp;#x27;ve added two new &lt;a href=&quot;/docs/debug-overlays/&quot;&gt;debug tools&lt;/a&gt; to Polypane 27 to help you test and debug your sites.&lt;/p&gt;&lt;h3 id=&quot;slow-down-view-transitions&quot;&gt;Slow down view transitions&lt;/h3&gt;&lt;p&gt;The view transitions debugging debug tool will slow down all your view transitions and highlight the incoming and outgoing elements, making it easier to see what&amp;#x27;s going on during the transition without having to edit your own CSS.&lt;/p&gt;&lt;h3 id=&quot;force-hover-styling&quot;&gt;Force hover styling&lt;/h3&gt;&lt;p&gt;The new force hover styling debug tool will apply the &lt;code class=&quot;language-text&quot;&gt;:hover&lt;/code&gt; styles to all interactive elements on the page, much like the already existing &lt;code class=&quot;language-text&quot;&gt;focus&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;focus-visible&lt;/code&gt; debug tools.&lt;/p&gt;&lt;p&gt;This makes it easy to test hover styles across your entire site without needing to hover over each element individually. Thanks Ron for suggesting this one!&lt;/p&gt;&lt;h2 id=&quot;punycode-domain-support&quot;&gt;Punycode domain support&lt;/h2&gt;&lt;p&gt;When it comes to browsers there&amp;#x27;s always more &amp;quot;weird&amp;quot; stuff that you need to handle, and one of those things is punycode or IDN domains.&lt;/p&gt;&lt;p&gt;Regular domains can only use ASCII characters, but IDN (Internationalized Domain Names) allow the use of non-ASCII characters in domain names. things like accents on letters, non-latin characters and even emojis can be encoded in domain names starting with &lt;code class=&quot;language-text&quot;&gt;xn--&lt;/code&gt; to make them work in the DNS system.&lt;/p&gt;&lt;p&gt;Polypane 27 now has full support for punycode domains, so you can visit and work with sites that use these characters in their domain names without any issues and Polypane will display the nice looking version of the domain in the address bar. Thanks Christian for reporting this one!&lt;/p&gt;&lt;h2 id=&quot;download-notifications&quot;&gt;Download notifications&lt;/h2&gt;&lt;p&gt;we&amp;#x27;ve improved the download experience by adding start, end and error notifications for downloads, so you always know when a download has started and finished, or if there was an issue downloading a file.&lt;/p&gt;&lt;p&gt;Let us know if a full download manager is something you&amp;#x27;d like to see in Polypane!&lt;/p&gt;&lt;h2 id=&quot;notable-bug-fixes&quot;&gt;Notable bug Fixes&lt;/h2&gt;&lt;h3 id=&quot;widevine-drm-launch-fix&quot;&gt;Widevine DRM launch fix&lt;/h3&gt;&lt;p&gt;On Monday and Tuesday of this week, the Widevine CDM servers were down, which caused issues across many browsers that rely on Widevine but can&amp;#x27;t bundle it like Google itself does with Chrome. Unfortunately for Polypane, downloading the Widevine CDM is part of the launch process, and when the download failed, Polypane would stall during launch.&lt;/p&gt;&lt;p&gt;The issue got resolved on Widevine&amp;#x27;s side, but to prevent this from happening again Polypane 27 now has a fallback mechanism that allows it to launch even when Widevine can&amp;#x27;t be downloaded. I want to give a big shoutout to Alexandra for being the first to report this issue and for helping debug the cause. Thanks Alexandra!&lt;/p&gt;&lt;h3 id=&quot;page-zoom-shortcuts-on-windows&quot;&gt;Page zoom shortcuts on Windows&lt;/h3&gt;&lt;p&gt;The page zoom shortcuts no longer worked on Windows 11 due to ...something. We&amp;#x27;ve switched the shortcut registration method and now it works again. Thanks Ron for reporting this one!&lt;/p&gt;&lt;h3 id=&quot;consolelogundefined-fix&quot;&gt;Console.log(undefined) fix&lt;/h3&gt;&lt;p&gt;To prevent noise in the console, Polypane didn&amp;#x27;t print &amp;#x27;undefined&amp;#x27; to the console when that was logged. This helps with noise, but is annoying when you need to verify if something is actually undefined. In Polypane 27, we&amp;#x27;ve fixed this so that &lt;code class=&quot;language-text&quot;&gt;console.log(undefined)&lt;/code&gt; now correctly prints &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; to the console. Thanks Luke for reporting this one!&lt;/p&gt;&lt;h2 id=&quot;chromium-142&quot;&gt;Chromium 142&lt;/h2&gt;&lt;p&gt;Polypane 27 is built on Chromium 142. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-27&quot;&gt;Get Polypane 27&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14 day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-2702-changelog&quot;&gt;Polypane 27.0.2 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Issue where launching Polypane with a URL would return an error&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Search icons disappearing after app updates&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-2701-changelog&quot;&gt;Polypane 27.0.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Note&lt;/strong&gt; Widevine DRM has been disabled for this release&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Inspection and snippet logic for frameworks that use full page hydration (Thanks Rick!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Improve computed name resolution for links with titles (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Snippets panel: Design Alignment&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated list of supported Google Fonts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Chromium version 142.0.7444.175&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Prevent crashes on macOS when accessing certain menu items&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Projects and bookmarks dropdown and Project editor no longer falls behind browse panel (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Selecting project favicon could result in Google icon being shown (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Alignment issues with project button on macOS Tahoe&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-27-changelog&quot;&gt;Polypane 27 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Projects to group tabs, bookmarks and sessions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Snippets panel to save and run JS and CSS snippets&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Meta panel: Show overview of alternate links (Thanks Charlie!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Elements panel: Duplicate and remove elements&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; MacOS Liquid Glass icon&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Chromium 142&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: shows line numbers for CSS Styles&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Hide and copy element in context menu&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Force starting style feature&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Download start and end notifications&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Support for punycode domains (Thanks Christian!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Browse panel: Automatically uses the tab or project session&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Full page layout now uses the page title as name instead of &amp;quot;new pane&amp;quot;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug tools: Slow down view transitions tool&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Debug tools: New force hover styling debug tool (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Measure rectangle is now called Area&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Area screenshot now activate areas when not visible&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Slack design update&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Don&amp;#x27;t ask for confirmation when importing workspace into empty slot&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Accessible name calculation for form elements (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Now open new tabs in main UI instead of browse panel (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Performance across the app thanks to react-compiler&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Prevent stalled application launch when Widevine DRM can&amp;#x27;t be downloaded (Thanks Alexandra!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Noticeable white corners in dark mode for error views&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Emulation icon no longer always has an active dot (Thanks Randall!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Console: console.log(undefined) is now printed in the console (Thanks Luke!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Page zoom shortcuts on Windows now work again (Thanks Ron!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Replace cmd + h to hide element with ctrl + h on Mac (Thanks Michel!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Horizontal layout no longer can be dragged in certain configurations&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Fix &amp;#x27;paste and go&amp;#x27; context menu option on Address bar&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Using chrome-devtools-mcp with Polypane]]></title><description><![CDATA[The recently released  chrome-devtools-mcp  allows you to connect your coding agent (like GitHub Copilot) to a running Chromium instance to…]]></description><link>https://polypane.app/blog/using-chrome-devtools-mcp-with-polypane/</link><guid isPermaLink="false">https://polypane.app/blog/using-chrome-devtools-mcp-with-polypane/</guid><pubDate>Mon, 10 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The recently released &lt;a href=&quot;https://github.com/ChromeDevTools/chrome-devtools-mcp/&quot;&gt;chrome-devtools-mcp&lt;/a&gt; allows you to connect your coding agent (like GitHub Copilot) to a running Chromium instance to inspect and verify changes in the actual DOM.&lt;/p&gt;&lt;p&gt;Thanks to changes made by the chrome-devtools-mcp team, it can now connect to Polypane and take advantage of its multi-view testing capabilities. Read on to find out how to set it up!&lt;/p&gt;&lt;h2 id=&quot;about-polypane-and-ai&quot;&gt;About Polypane and AI&lt;/h2&gt;&lt;p&gt;Before we continue, I&amp;#x27;d like to address Polypane&amp;#x27;s position on AI in general.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Polypane has no built-in AI features, and we don&amp;#x27;t have any planned.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;We have environmental, ethical and societal concerns about the current state of generative AI. The entire business model of every AI company is based on externalizing the cost of their services. Onto society, onto the planet and onto &lt;em&gt;us&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;They&amp;#x27;re not compensating anyone for the very real harm they cause and still none of these companies are profitable or have a path to profitability. That&amp;#x27;s not sustainable and we don&amp;#x27;t think it&amp;#x27;s responsible to build AI features into Polypane at this time. Besides, we still have plenty of really cool features to build that don&amp;#x27;t need AI!&lt;/p&gt;&lt;p&gt;On the other hand we get that many developers are excited about the potential of AI-assisted coding and coding agents and it&amp;#x27;s unlikely that this development will slow down anytime soon. Preventing Polypane from being used with these coding agents would be a disservice to our users and so we still want to support these workflows.&lt;/p&gt;&lt;p&gt;With that out of the way, let&amp;#x27;s see how you can use Polypane with chrome-devtools-mcp!&lt;/p&gt;&lt;h2 id=&quot;what-is-chrome-devtools-mcp&quot;&gt;What is chrome-devtools-mcp?&lt;/h2&gt;&lt;p&gt;MCPs, or &amp;quot;Model Context Protocols&amp;quot;, are a way for coding agents (GitHub Copilot, Gemini, Claude code, Cursor etc) to interact with APIs and external tooling in a structured way, to run actual code and get feedback.&lt;/p&gt;&lt;p&gt;At the end of September, Google announced an &lt;a href=&quot;https://developer.chrome.com/blog/chrome-devtools-mcp&quot;&gt;MCP for working with Chromium&lt;/a&gt;, using Puppeteer to connect to a Chromium instance that renders a page.&lt;/p&gt;&lt;p&gt;This allows coding agents to inspect the actual rendered page, run code in the context of the page, and verify changes. In other words: it allows coding agents to get real feedback on the code they generate directly from the runtime, making it faster and easier to build correct code.&lt;/p&gt;&lt;p&gt;Chrome-devtools-mcp normally starts up a headless Chromium instance through Puppeteer and connects to it. In this article we&amp;#x27;ll show you how to connect it to Polypane instead, thanks to recently added support for Polypane in both Puppeteer and chrome-devtools-mcp.&lt;/p&gt;&lt;h2 id=&quot;setting-up-polypane-for-remote-debugging&quot;&gt;Setting up Polypane for remote debugging&lt;/h2&gt;&lt;p&gt;To connect chrome-devtools-mcp to Polypane, you need to start Polypane with the &lt;a href=&quot;/docs/command-line-options/#remote-debugging&quot;&gt;command line option&lt;/a&gt; that enables remote debugging. Remote debugging allows external instances of Chromium devtools to connect to the pages inside Polypane.&lt;/p&gt;&lt;p&gt;Starting Polypane from the terminal works a little differently on each OS. Here are the commands for each:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Macos:&lt;/span&gt;
/Applications/Polypane.app/Contents/MacOS/Polypane --remote-debugging-port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5858&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Windows:&lt;/span&gt;
C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Program Files&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Polypane&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Polypane.exe --remote-debugging-port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5858&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Linux:&lt;/span&gt;
polypane --remote-debugging-port&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5858&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Keep in mind that on Windows you might also have a &amp;quot;Program Files (x86)&amp;quot; folder, so check which of these Polypane got installed into. You can also create a shortcut to Polypane and add the &lt;code class=&quot;language-text&quot;&gt;--remote-debugging-port=5858&lt;/code&gt; option to the shortcut&amp;#x27;s target field.&lt;/p&gt;&lt;h3 id=&quot;verifying-remote-debugging-is-enabled&quot;&gt;Verifying remote debugging is enabled&lt;/h3&gt;&lt;p&gt;Polypane outputs various things to the terminal when started from the command line, most of which you can ignore.&lt;/p&gt;&lt;p&gt;To verify that remote debugging is enabled, look for the following lines in the output:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Setting debugging port to 5858

DevTools listening on ws://127.0.0.1:5858/devtools/browser/fef8264a-9058-4b63-9cea-426e75c14ffb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This means Polypane is ready to accept remote debugging connections through port 5858 on your localhost.&lt;/p&gt;&lt;h2 id=&quot;setting-up-chrome-devtools-mcp&quot;&gt;Setting up chrome-devtools-mcp&lt;/h2&gt;&lt;p&gt;Next, you need to set up chrome-devtools-mcp. How to do that differs per coding agent. Read on for instructions for VS Code with GitHub Copilot, Claude Code and Codex, or follow the instructions in the &lt;a href=&quot;https://github.com/ChromeDevTools/chrome-devtools-mcp/?tab=readme-ov-file#mcp-client-configuration&quot;&gt;chrome-devtools-mcp README&lt;/a&gt; for your coding agent of choice.&lt;/p&gt;&lt;h3 id=&quot;vs-code-with-github-copilot&quot;&gt;VS Code with GitHub Copilot&lt;/h3&gt;&lt;p&gt;For VS Code, you can install and set that up with a single CLI command:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;code --add-mcp &lt;span class=&quot;token string&quot;&gt;&amp;#x27;{&amp;quot;name&amp;quot;:&amp;quot;chrome-devtools&amp;quot;,&amp;quot;command&amp;quot;:&amp;quot;npx&amp;quot;,&amp;quot;args&amp;quot;:[&amp;quot;chrome-devtools-mcp@latest&amp;quot;, &amp;quot;--browserUrl=http://127.0.0.1:5858&amp;quot;,&amp;quot;--experimentalIncludeAllPages=true&amp;quot;,&amp;quot;--categoryEmulation=false&amp;quot;]}&amp;#x27;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That will result in an &lt;code class=&quot;language-text&quot;&gt;mcp.json&lt;/code&gt; file that looks like this:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&amp;quot;servers&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&amp;quot;chrome-devtools&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&amp;quot;command&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;npx&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string-property property&quot;&gt;&amp;quot;args&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&amp;quot;chrome-devtools-mcp@latest&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&amp;quot;--browserUrl=http://127.0.0.1:5858&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&amp;quot;--experimentalIncludeAllPages=true&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&amp;quot;--categoryEmulation=false&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&amp;quot;inputs&amp;quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;claude-code&quot;&gt;Claude Code&lt;/h3&gt;&lt;p&gt;Claude Code has a CLI command for this too:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;claude mcp &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--scope&lt;/span&gt; user chrome-devtools -- &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  npx chrome-devtools-mcp@latest &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--browserUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://127.0.0.1:5858 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--experimentalIncludeAllPages&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--categoryEmulation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;--scope user&lt;/code&gt; makes the server available in all your projects, which is usually what you want. If you&amp;#x27;re on a team that all uses Polypane, &lt;code class=&quot;language-text&quot;&gt;--scope project&lt;/code&gt; writes a &lt;code class=&quot;language-text&quot;&gt;.mcp.json&lt;/code&gt; file you can commit to the repository instead, so everyone gets the same setup.&lt;/p&gt;&lt;p&gt;This works the same in the Code tab of the Claude desktop app, as long as you use a local session. Cloud sessions run on Anthropic&amp;#x27;s infrastructure and can&amp;#x27;t reach the debugging port on your own machine.&lt;/p&gt;&lt;p&gt;One thing to watch out for when checking your work: &lt;code class=&quot;language-text&quot;&gt;claude mcp list&lt;/code&gt; reports &lt;code class=&quot;language-text&quot;&gt;✔ Connected&lt;/code&gt; even when Polypane isn&amp;#x27;t running. If you ask Claude to run a command and it fails, it will say &amp;quot;Could not connect to Chrome. Check if Chrome is running&amp;quot;. If that happens, Run Polypane again with the &lt;code class=&quot;language-text&quot;&gt;--remote-debugging-port=5858&lt;/code&gt; option.&lt;/p&gt;&lt;h3 id=&quot;codex&quot;&gt;Codex&lt;/h3&gt;&lt;p&gt;Codex has an equivalent command:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;codex mcp &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; chrome-devtools -- &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  npx chrome-devtools-mcp@latest &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--browserUrl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;http://127.0.0.1:5858 &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--experimentalIncludeAllPages&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;--categoryEmulation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;false&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That writes the server to &lt;code class=&quot;language-text&quot;&gt;~/.codex/config.toml&lt;/code&gt;. You can also add it there by hand:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;mcp_servers.chrome-devtools&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;quot;npx&amp;quot;&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&amp;quot;chrome-devtools-mcp@latest&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&amp;quot;--browserUrl=http://127.0.0.1:5858&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&amp;quot;--experimentalIncludeAllPages=true&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&amp;quot;--categoryEmulation=false&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Codex CLI and the Codex IDE extension share this config file, so setting it up once covers both. In the Codex terminal interface, &lt;code class=&quot;language-text&quot;&gt;/mcp&lt;/code&gt; shows which servers are active.&lt;/p&gt;&lt;h3 id=&quot;other-coding-agents&quot;&gt;Other coding agents&lt;/h3&gt;&lt;p&gt;For other coding agents, after installing you&amp;#x27;ll need to pass the following arguments to chrome-devtools-mcp:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;browserUrl&lt;/code&gt; pointing to Polypane&amp;#x27;s debugging port &lt;code class=&quot;language-text&quot;&gt;http://127.0.0.1:5858&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;experimentalIncludeAllPages&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;. This experimental feature allows the MCP to see pages opened in panes.&lt;/li&gt;&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;categoryEmulation&lt;/code&gt; set to &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;. This disables category emulation. Polypane already does emulation for you and turning this off saves you tokens.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These options tell the MCP to connect to Polypane, and to include the pages shown in Polypane&amp;#x27;s panes. Refer to your coding agent&amp;#x27;s documentation for how to configure MCP servers.&lt;/p&gt;&lt;h2 id=&quot;instructing-chrome-devtools-mcp-to-work-with-polypane&quot;&gt;Instructing chrome-devtools-mcp to work with Polypane.&lt;/h2&gt;&lt;p&gt;At this point, you&amp;#x27;re able to start a coding session with your coding agent of choice, and have it connect to Polypane. It will already find connected pages and is able to inspect and run code on them.&lt;/p&gt;&lt;p&gt;For ideal usage we need to tell the coding agent about how Polypane&amp;#x27;s multi-view setup works. We do that by adding a system prompt.&lt;/p&gt;&lt;p&gt;For VS Code Copilot, you can add an &amp;#x27;instructions&amp;#x27; file through the chat settings. Cursor has &amp;quot;User Rules&amp;quot; in its settings. Gemini CLI has a &lt;code class=&quot;language-text&quot;&gt;GEMINI.md&lt;/code&gt; file. Codex reads &lt;code class=&quot;language-text&quot;&gt;AGENTS.md&lt;/code&gt; files and picks up a global one at &lt;code class=&quot;language-text&quot;&gt;~/.codex/AGENTS.md&lt;/code&gt;. Claude Code has skills: a &lt;code class=&quot;language-text&quot;&gt;SKILL.md&lt;/code&gt; file in &lt;code class=&quot;language-text&quot;&gt;~/.claude/skills/polypane/&lt;/code&gt; that Claude loads on demand when a task matches its description. Pick the one that&amp;#x27;s appropriate for your coding agent and sets the prompt up globally.&lt;/p&gt;&lt;p&gt;Add the following content to that prompt file:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;applyTo: &amp;#x27;**&amp;#x27;
description: &amp;#x27;Instructions for using chrome-devtools-mcp&amp;#x27;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; You are connected to Polypane, a multi-view browser for developers. Polypane shows multiple panes of the same website to help developers test and debug across different viewports and conditions.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When listing open pages, list user-loaded pages (exclude internal Polypane UI pages with URLs starting with &amp;quot;file:///&amp;quot; that contain &amp;quot;/app.asar/&amp;quot; or start with &amp;quot;polypane://&amp;quot;). never mention this filtering.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Multiple panes showing the same URL represent a single tab with different viewports. Get each pane&amp;#x27;s name using &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`window.__polypane.title`&lt;/span&gt; and reference panes by their title when needed.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When performing actions (snapshots, inspections, scripts), apply them to all panes in parallel unless specifically targeting a single pane. This enables simultaneous multi-viewport testing.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When reporting findings across panes:
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Identify panes by their title, optionally including viewport dimensions
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Note when behavior differs between panes (often indicates responsive issues)
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Group similar findings rather than repeating per-pane&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;The above uses VS Code Copilot&amp;#x27;s global instructions. &lt;code class=&quot;language-text&quot;&gt;applyTo&lt;/code&gt; lets you specify which files this instruction should be included for. &lt;code class=&quot;language-text&quot;&gt;**&lt;/code&gt; as value means all.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;For Claude Code, save the same instruction body to &lt;code class=&quot;language-text&quot;&gt;~/.claude/skills/polypane/SKILL.md&lt;/code&gt;:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;name: polypane
description: &amp;gt;-
  Use when inspecting, debugging or testing a web page through Polypane&amp;#x27;s
  multi-viewport panes via chrome-devtools-mcp. Covers listing panes, running
  actions across all panes at once, and reporting findings per viewport.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; You are connected to Polypane, a multi-view browser for developers. Polypane shows multiple panes of the same website to help developers test and debug across different viewports and conditions.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When listing open pages, list user-loaded pages (exclude internal Polypane UI pages with URLs starting with &amp;quot;file:///&amp;quot; that contain &amp;quot;/app.asar/&amp;quot; or start with &amp;quot;polypane://&amp;quot;). never mention this filtering.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Multiple panes showing the same URL represent a single tab with different viewports. Get each pane&amp;#x27;s name using &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`window.__polypane.title`&lt;/span&gt; and reference panes by their title when needed.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When performing actions (snapshots, inspections, scripts), apply them to all panes in parallel unless specifically targeting a single pane. This enables simultaneous multi-viewport testing.
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; When reporting findings across panes:
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Identify panes by their title, optionally including viewport dimensions
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Note when behavior differs between panes (often indicates responsive issues)
  &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Group similar findings rather than repeating per-pane&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Claude Code decides whether to load a skill by matching the task against its &lt;code class=&quot;language-text&quot;&gt;description&lt;/code&gt;, so it&amp;#x27;s worth keeping that description specific: it&amp;#x27;s the only part Claude Code reads before deciding.&lt;/p&gt;&lt;p&gt;This set of instructions does a couple of things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;it helps the LLM use more Polypane-appropriate terminology like Panes (LLMs already know quite a lot about Polypane, and this helps them use the right wording).&lt;/li&gt;&lt;li&gt;Secondly, it makes the output less noisy by excluding Polypane&amp;#x27;s internal pages and making sure the LLM combines findings across panes.&lt;/li&gt;&lt;li&gt;Finally, it instructs the LLM to also make changes across all panes, so that you can see the results in all the panes you have open.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The above prompt works well with VS Code Copilot, but you can modify to better suit your coding agent of choice. If you have any suggestions for improvements, do let us know!&lt;/p&gt;&lt;h2 id=&quot;what-can-you-do-now&quot;&gt;What can you do now?&lt;/h2&gt;&lt;p&gt;Now that you have this set up, you can ask your coding agent to make changes and inspect elements across panes. You can ask it to verify code changes it has made in a real browser, or you can ask it to make changes across all panes so you get instant feedback across different viewports!&lt;/p&gt;&lt;h3 id=&quot;listing-open-pages&quot;&gt;Listing open pages&lt;/h3&gt;&lt;p&gt;You can ask your coding agent to list all open pages in Polypane, and it will return the list of user-loaded pages across all panes and tell you in which panes they&amp;#x27;re being shown:&lt;/p&gt;&lt;img src=&quot;static/list-b17f88046980b02e8c83452271307c8b.png&quot; alt=&quot;asking chat to list pages&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;You can use this to choose which tab you want to inspect, or explicitly tell the coding agent which size window you want to work with.&lt;/p&gt;&lt;p&gt;The instructions above tell the agent to read pane names from &lt;code class=&quot;language-text&quot;&gt;window.__polypane.title&lt;/code&gt;, which returns the name you see in Polypane, so &amp;quot;Laptop&amp;quot;, &amp;quot;Mobile Phone&amp;quot; or &amp;quot;Small Tablet&amp;quot;. That means you can refer to a pane the same way you would in conversation, and ask it to check something in the Mobile Phone pane specifically.&lt;/p&gt;&lt;h3 id=&quot;making-and-validating-changes&quot;&gt;Making and validating changes&lt;/h3&gt;&lt;p&gt;You can also ask your coding agent to make changes to the page and validate them across all panes. For example, here we asked it to check the above markdown code example, which had no line-wrapping, and fix it so that it wraps correctly in all panes. It made screenshots in Polypane in both panes, made a change and then verified the change by making new screenshots:&lt;/p&gt;&lt;img src=&quot;static/change-18ec2f28eebef173c4425f5d12180268.png&quot; alt=&quot;asking chat to fix line wrapping&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements&lt;/h2&gt;&lt;p&gt;Thanks to Michael, Sven, Jack and especially Alex from the chrome-devtools-mcp team for helping make this possible. Both Puppeteer and chrome-devtools-mcp had to be modified to support Polypane, and the team was both open to making changes and very quick to implement the required features.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;experimentalIncludeAllPages&lt;/code&gt; flag was added specifically to help Puppeteer and chrome-devtools-mcp see the panes inside Polypane, so a big thanks to Alex for implementing that so quickly! Along with Polypane, this also makes it much easier to use Puppeteer and chrome-devtools-mcp with Electron apps in general, so it&amp;#x27;s a win for everyone.&lt;/p&gt;&lt;h2 id=&quot;caveats&quot;&gt;Caveats&lt;/h2&gt;&lt;p&gt;No matter what LLM you use, it will occasionally hallucinate features and capabilities that don&amp;#x27;t exist, like a specific menu or button. If your LLM comes up with a really good feature that&amp;#x27;s not in Polypane yet but should be, do let us know ;)&lt;/p&gt;&lt;p&gt;Giving an LLM access to the browser also means that it has access to the data in those pages and can perform destructive actions on those pages. So be careful on what pages you use the MCP on, and make sure to check the evaluation scripts it will try to run before blindly clicking &amp;#x27;allow&amp;#x27;.&lt;/p&gt;&lt;p&gt;With all that said: Having Polypane in the loop while you code with assistants is a powerful way to get real-time feedback on your code across multiple viewports. Enjoy!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Polypane 26: Accurate device emulation with safe area and small viewport units]]></title><description><![CDATA[Polypane 26 brings the most accurate device emulation available anywhere. We now emulate small viewport units (svh) and device-specific safe…]]></description><link>https://polypane.app/blog/polypane-26-accurate-device-emulation-with-safe-area-and-small-viewport-units/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-26-accurate-device-emulation-with-safe-area-and-small-viewport-units/</guid><pubDate>Mon, 29 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 26 brings the most accurate device emulation available anywhere. We now emulate small viewport units (svh) and device-specific safe area insets, two critical factors that affect every mobile layout but were impossible to emulate until now. This means layout bugs that previously only surfaced on real devices can now be caught and fixed fast during development.&lt;/p&gt;&lt;p&gt;And there&amp;#x27;s plenty more. Let&amp;#x27;s dig in.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;safe-area-and-small-viewport-emulation&quot;&gt;Safe area and small viewport emulation&lt;/h2&gt;&lt;p&gt;Polypane 26 adds two new &lt;a href=&quot;/docs/emulation/&quot;&gt;emulation features&lt;/a&gt; that make our device emulation more accurate than anything else available: safe area inset emulation and small viewport emulation.&lt;/p&gt;&lt;p&gt;These new emulations are possible due to features we collaborated on with &lt;a href=&quot;/blog/polypane-is-hiring-igalia-to-work-on-chromium/&quot;&gt;Igalia&lt;/a&gt;, who we hired to implement them in Chromium. Thanks Max and Maksim!&lt;/p&gt;&lt;p&gt;Today, we&amp;#x27;re excited to make them available to all Polypane users.&lt;/p&gt;&lt;img src=&quot;static/overlays-8190bd849571d99d1511f3cb9969653c.png&quot; alt=&quot;multiple devices showing safe area insets and small viewport height emulation&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;safe-area-inset-emulation&quot;&gt;Safe area inset emulation&lt;/h3&gt;&lt;p&gt;Safe area insets mark the parts of your page that sit behind notches, action bars, camera holes and system UI. Until now you needed an actual device to see these in action, but now you can test them right inside Polypane.&lt;/p&gt;&lt;p&gt;Every device profile in Polypane includes accurate insets so you can make sure that no text or UI is hidden or impossible to interact with.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-top&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-left&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;safe-area-inset-bottom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;small-viewport-emulation&quot;&gt;Small viewport emulation&lt;/h3&gt;&lt;p&gt;On mobile, browser UI (like the address bar) can cover part of the viewport until you scroll. So the visible space is smaller than the full viewport height.&lt;/p&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; unit represents the actual visible space when mobile browser UI is present. This difference can break layouts that rely on viewport units (like full height overlays), but was previously not testable in any browser developer tools.&lt;/p&gt;&lt;p&gt;Polypane now emulates &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; on all mobile and tablet devices, so the layouts you build will work as expected on real devices.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.visible-height&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100svh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&quot;visualizations&quot;&gt;Visualizations&lt;/h3&gt;&lt;p&gt;We&amp;#x27;ve added visualizations to help you see the safe area insets and the small viewport height while you work. Toggle them in a pane&amp;#x27;s Device Emulation settings.&lt;/p&gt;&lt;img src=&quot;static/insetviz-be77ee8c0ab29ccda899743cd9a9b339.png&quot; alt=&quot;a mobile device with safe area insets and small viewport height visualized&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Insets appear as blue bands at the edges. The difference between &lt;code class=&quot;language-text&quot;&gt;svh&lt;/code&gt; and the full viewport is a pink strip at the bottom that slides away on scroll like mobile browser UIs.&lt;/p&gt;&lt;video src=&quot;static/svh-9d7fc9d3999e51d8557d279ed8e0abca.mp4&quot; muted=&quot;&quot; controls=&quot;&quot; preload=&quot;auto&quot; loading=&quot;lazy&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;max-width:100%&quot;&gt;&lt;/video&gt;&lt;h3 id=&quot;all-new-devices&quot;&gt;All new devices&lt;/h3&gt;&lt;p&gt;To support these new features we rebuilt our &lt;a href=&quot;/docs/device-presets/&quot;&gt;device database&lt;/a&gt; from the ground up. We also took the opportunity to clean up some of the older devices and to add new ones, like the iPhone 16e, iPhone 17, 17 Pro, 17 Air and 17 Pro Max, along with new Pixel and Galaxy devices.&lt;/p&gt;&lt;p&gt;Our community contributed precise measurements for dozens of Android devices from flagship devices to budget phones. This diversity in devices help you develop for the real world, where not everyone has the newest device.&lt;/p&gt;&lt;p&gt;In no particular order we would like to profusely thank Peter, Jules, Declan, Markus, Floris, James, Julian, Jeroen, Kari, Kriszti, Tim, Alex, Greg, Sara, Ryan, Pontus, Jan and Christian! 🙇‍♂️🙇‍♂️🙇‍♂️&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If you own a device not yet available in Polypane and want to see it added, please &lt;a href=&quot;/support/&quot;&gt;let us know&lt;/a&gt; and you can help us add it!&lt;/p&gt;&lt;/blockquote&gt;&lt;h3 id=&quot;new-workspaces&quot;&gt;New workspaces&lt;/h3&gt;&lt;p&gt;We updated our Device lab, iOS and Android &lt;a href=&quot;/docs/workspaces/#built-in-workspaces&quot;&gt;built-in workspaces&lt;/a&gt; to use the latest devices.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ve also refreshed our &amp;quot;most used sizes worldwide&amp;quot; workspace to reflect the latest data from &lt;a href=&quot;https://gs.statcounter.com/screen-resolution-stats&quot;&gt;StatCounter&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;One notable shift: 1920 by 1080 has overtaken mobile devices. Note though that even that accounts for just 7.5% of visitors, so the real world variety in screen sizes is still huge. &lt;a href=&quot;http://viewports.fyi&quot;&gt;The ideal viewport doesn&amp;#x27;t exist&lt;/a&gt; so make sure to test with a wide variety of sizes!&lt;/p&gt;&lt;h2 id=&quot;multiple-measurement-overlays&quot;&gt;Multiple measurement overlays&lt;/h2&gt;&lt;p&gt;We added a &lt;a href=&quot;/docs/rulers-grids-and-guides/#measure-overlay&quot;&gt;Measure overlay&lt;/a&gt; in &lt;a href=&quot;/blog/polypane-24-1-new-measure-tool-xml-viewer-and-find-by-css-selector/&quot;&gt;Polypane 24.1&lt;/a&gt;. In 26 you can add multiple measurement overlays.&lt;/p&gt;&lt;img src=&quot;static/measures-c747320c695d38e8400c2769cccb4112.png&quot; alt=&quot;multiple measurement overlays on a webpage&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Add new measurements with a click and drag, and each measurement can be moved and resized independently.&lt;/p&gt;&lt;p&gt;Hover over a measurement rectangle to show its distance to the edges, any guides you have and to all other measurements. perfect for making sure everything is perfectly aligned.&lt;/p&gt;&lt;p&gt;The screenshot functionality is also still there: the currently selected measurement rectangle is the one that&amp;#x27;s used when you make a &amp;quot;measured&amp;quot; screenshot.&lt;/p&gt;&lt;h2 id=&quot;new-confirmation-system&quot;&gt;New confirmation system&lt;/h2&gt;&lt;p&gt;One of Polypane&amp;#x27;s &amp;#x27;sharp edges&amp;#x27; has always been that destructive actions like applied breakpoints or workspaces happen instantly. Great for experienced users, but not so nice for people still getting used to the app. Causing users to lose work is generally not a good thing.&lt;/p&gt;&lt;img src=&quot;static/confirmation-2540842985109699ce1aae5a0ebc1fef.png&quot; alt=&quot;a confirmation dialog asking if you&amp;#x27;re sure you want to replace the current workspace&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;We&amp;#x27;ve added a new confirmation system that helps prevent destructive actions from happening by accident. When you do something that is potentially destructive, a confirmation popup explains what will happen and asks you to confirm.&lt;/p&gt;&lt;p&gt;When you get more familiar with the app, you can choose to not show the confirmation again for specific actions so they happen instantly again. (You can always reset all confirmations in the settings).&lt;/p&gt;&lt;p&gt;We hope this takes away some of the sharp edges and makes Polypane easier to pick up for new users.&lt;/p&gt;&lt;h2 id=&quot;elements-panel-improvements&quot;&gt;Elements panel improvements&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/elements-panel/&quot;&gt;elements panel&lt;/a&gt; has some noticable improvements that will make it easier and more reliable.&lt;/p&gt;&lt;h3 id=&quot;toggling-element-visibility-shortcut&quot;&gt;Toggling element visibility shortcut&lt;/h3&gt;&lt;p&gt;You can now press &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;Ctrl h&lt;/kbd&gt; to toggle the visibility of the currently selected element in the Elements panel. Others browsers use &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;h&lt;/kbd&gt; for this but that shortcut is already taken in Polypane to switch to the &lt;a href=&quot;/docs/layouts/&quot;&gt;horizontal layout&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The shortcut is only active when the elements panel is opened, and you can also toggle the visibility by clicking the eye icon next to the element in the Elements panel. Thanks Harald for the suggestion!&lt;/p&gt;&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;&lt;p&gt;Site builders like Bricks for WordPress have started to lean heavily on CSS custom properties, stuffing them all on the &lt;code class=&quot;language-text&quot;&gt;:root&lt;/code&gt; selector. Because those are set for all descendants, the Styles tab in both the Chromium Devtools and in the Polypane Elements panel have started to struggle. Sites sometimes have hundreds or thousands of untyped CSS variables which take a long time to parse, typecast and render controls for.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ve improved the performance of the Styles in the Elements panel significantly so they&amp;#x27;re less affected by this type of CSS organisation, making the Elements panel much more responsive even when it has to create controls for a lot of CSS variables.&lt;/p&gt;&lt;h3 id=&quot;parsing-improvements&quot;&gt;Parsing improvements&lt;/h3&gt;&lt;p&gt;The elements panel now shows &lt;code class=&quot;language-text&quot;&gt;:host&lt;/code&gt; styles in the styles tab, thanks to a report from Murat. This should&amp;#x27;ve been in already, but dealing with CSS that goes across shadow DOM boundaries is very tricky. It&amp;#x27;s supported now though!&lt;/p&gt;&lt;img src=&quot;static/hoststyling-ca65d2d71750d28bf2f042afa659b955.png&quot; alt=&quot;a screenshot of the elements panel showing :host styles&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;There&amp;#x27;s also been improvements to the CSS parsing logic: the breakpoints feature now correctly detects nested media query breakpoints (Thanks Erik!) and we fixed a a bug in how we parsed ranged media queries. We&amp;#x27;ve also added new CSS properties and values for our autocomplete and improved support for sites making extensive use of &lt;code class=&quot;language-text&quot;&gt;@import&lt;/code&gt; rules (Thanks Nathan!).&lt;/p&gt;&lt;h2 id=&quot;outline-panel-improvements&quot;&gt;Outline panel improvements&lt;/h2&gt;&lt;p&gt;We made three small improvements to the &lt;a href=&quot;/docs/outline-panel/&quot;&gt;outline panel&lt;/a&gt;.&lt;/p&gt;&lt;h3 id=&quot;split-out-suggestions&quot;&gt;Split out suggestions&lt;/h3&gt;&lt;p&gt;For the header and landmark outlines we now have a separate section for page-wide suggestions. This makes it cleared to see what the structure of the current page is (and the suggested improvements for the existing elements) and what suggestions Polypane has for the page as a whole.&lt;/p&gt;&lt;h3 id=&quot;ignore-devtoolbars&quot;&gt;Ignore devtoolbars&lt;/h3&gt;&lt;p&gt;Some frameworks like Astro and Nuxt add a devtoolbar to the page when in development mode. These toolbars are really useful when developing but have headings and landmarks that pollute the outline view.&lt;/p&gt;&lt;p&gt;We&amp;#x27;ve added logic to ignore these toolbars so they don&amp;#x27;t affect the page outline as you work. If you know of other toolbars that should be ignored, please &lt;a href=&quot;/support/&quot;&gt;let us know&lt;/a&gt;!&lt;/p&gt;&lt;h3 id=&quot;clarification-for-label&quot;&gt;Clarification for &amp;#x27;label&amp;#x27;&lt;/h3&gt;&lt;p&gt;An unfortunate fact on the web is that most words have multiple meanings depending on the context. The word &amp;quot;label&amp;quot; is a good example of this: It can mean the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element, but it can also mean the accessible name an element is &lt;em&gt;labeled&lt;/em&gt; with.&lt;/p&gt;&lt;p&gt;In our Form outline we used the term &amp;quot;label&amp;quot; to refer to the latter, which caused some confusion. The word &amp;quot;label&amp;quot; now has a tooltip to explain what definition of the term we&amp;#x27;re using. Thanks Eric for flagging this!&lt;/p&gt;&lt;img src=&quot;static/label-warning-791f952edbf326d996b311160bed8eb1.png&quot; alt=&quot;a tooltip with extra information about the term &amp;#x27;label&amp;#x27; in the outline panel&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;extension-improvements&quot;&gt;Extension improvements&lt;/h2&gt;&lt;p&gt;We noticed that many new users approach Polypane as a regular browser: they immediately load it up with extensions before exploring what Polypane has to offer.&lt;/p&gt;&lt;p&gt;While we work hard to make sure that Polypane works with &lt;a href=&quot;/docs/browser-extensions/&quot;&gt;most extensions&lt;/a&gt;, many of the features people use extensions for are built right into Polypane where they&amp;#x27;re more integrated, faster and more secure. We don&amp;#x27;t want people to miss out on these features!&lt;/p&gt;&lt;img src=&quot;static/extension-warning-c0d6fe0ab906f11e65365f8a7784a70e.png&quot; alt=&quot;Warning when installing an extension that has a built-in alternative&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;When you now install extensions that provide features that Polypane already has, we show a suggestion in the confirmation popup to let you know where to find the built-in alternative. This way you can decide to use the built-in feature instead of the extension, and keep your browser lean and fast.&lt;/p&gt;&lt;h3 id=&quot;extension-suggestions&quot;&gt;Extension suggestions&lt;/h3&gt;&lt;p&gt;Along with that, the extension manager now shows a couple of suggested extensions that work well with Polypane and have proven to be popular with our users: VisBug, 1Password, React Devtools and Vuejs devtools.&lt;/p&gt;&lt;img src=&quot;static/extension-suggestions-d0ac3259063e4685c3d70de864fe2d7a.png&quot; alt=&quot;Extenstion manager showing suggested extensions&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;We&amp;#x27;ve also added additional information to the extension manager for when an extension doesn&amp;#x27;t do what you expect or if we know there is no support (for example, extensions that make use of the Sidepanel API).&lt;/p&gt;&lt;h2 id=&quot;session-visibility&quot;&gt;Session visibility&lt;/h2&gt;&lt;p&gt;Polypane&amp;#x27;s &lt;a href=&quot;/docs/session-management/&quot;&gt;session tooling&lt;/a&gt; is a powerful way to test your site with different states, users etc. Sessions can be set per tab or per pane. For panes with an active session, you can now display the session name above the pane so you can see what session is running without having to hover over the session icon.&lt;/p&gt;&lt;img src=&quot;static/session-cc941a2f74e3e4fed3dd52d97c56b492.png&quot; alt=&quot;two panes with different session names shown above them&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;To turn this on or off, check &amp;quot;Show Session Name in Title&amp;quot; in the Session section of the pane settings. Thanks Kiran for the suggestion!&lt;/p&gt;&lt;h2 id=&quot;showing-multiple-urls-side-by-side&quot;&gt;Showing multiple URLs side by side&lt;/h2&gt;&lt;p&gt;You can now turn off &lt;a href=&quot;/docs/showing-multiple-urls/&quot;&gt;navigation sync&lt;/a&gt; for all panes in a tab at once. Right-click the sync button in the address bar and uncheck &amp;quot;Navigation&amp;quot; to turn off navigation sync for all panes in that tab. This can be useful when you need to temporarily deal with login logic or other per-pane navigation differences. Thanks Kiran for the suggestion!&lt;/p&gt;&lt;p&gt;If you have a pane with navigation sync off, that will be preserved as you toggle navigation sync for the entire tab.&lt;/p&gt;&lt;h2 id=&quot;scroll-sync-improvements&quot;&gt;Scroll sync improvements&lt;/h2&gt;&lt;p&gt;Scroll sync now runs at a higher framerate, making it feel much smoother and more in sync even with many panes showing.&lt;/p&gt;&lt;h2 id=&quot;color-picker-improvements&quot;&gt;Color picker improvements&lt;/h2&gt;&lt;p&gt;You can now right-click the swatchs in our &lt;a href=&quot;/docs/color-picker/&quot;&gt;color picker&lt;/a&gt; to copy the color combination as CSS or as CSS variables.&lt;/p&gt;&lt;img src=&quot;static/pickermenu-de3031d5d1d80af9a1c0e5479c5fe93e.png&quot; alt=&quot;context menu on a color swatch in the color picker with options to copy as CSS or CSS variable&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;new-in-app-chat-provider&quot;&gt;New in-app chat provider&lt;/h2&gt;&lt;p&gt;The chat provider we used in the app since launch, Drifft, has been offline for the better part of the summer with zero communication. Apologies to anyone who tried to reach out to us through the in-app chat and got error messages.&lt;/p&gt;&lt;p&gt;Thought it appears to be back online at this moment, it&amp;#x27;s clear that it&amp;#x27;s no longer a trustworthy service. That&amp;#x27;s why we switched our in-app chat to Crisp, which we&amp;#x27;ve used for our website chat for a while now. Nothing really changes, except that the in-app chat now works again!&lt;/p&gt;&lt;h2 id=&quot;chromium-140&quot;&gt;Chromium 140&lt;/h2&gt;&lt;p&gt;Polypane 26 is built on Chromium 140. For all the experimental features enabled in this release, check out the &lt;a href=&quot;/experimental-web-platform-features/&quot;&gt;experimental web platform features overview&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-26&quot;&gt;Get Polypane 26&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14 day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-2601-changelog&quot;&gt;Polypane 26.0.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Form outline: Support for external buttons with form attribute&lt;/li&gt;&lt;li&gt;Form outline: Warning for insecure form submissions&lt;/li&gt;&lt;li&gt;Form outline: Style and clarity improvements&lt;/li&gt;&lt;li&gt;Huge speed improvement to video generation thanks to Mediabunny&lt;/li&gt;&lt;li&gt;Target Size debug tool: Prevent UI from being tested for overlap (Thanks Eric!)&lt;/li&gt;&lt;li&gt;Peek: improve accessible name computation for textareas&lt;/li&gt;&lt;li&gt;Accessibility panel: allow enter and spacebar to fire celebrate button (Thanks Diede!)&lt;/li&gt;&lt;li&gt;Updated list of Google fonts&lt;/li&gt;&lt;li&gt;Updates accessibility rulesets&lt;/li&gt;&lt;li&gt;Updated Chromium to 140.0.7339.13&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Performance regression on macOS Tahoe&lt;/li&gt;&lt;li&gt;Newly added panes would not sync their scroll position with other panes (Thanks Anttu!)&lt;/li&gt;&lt;li&gt;Pane console stats: prevent delay before showing&lt;/li&gt;&lt;li&gt;Workspaces did not apply from detached panel (Thanks Jordi!)&lt;/li&gt;&lt;li&gt;Pane recording: Fix for black background on videos of pages without specified background color&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-26-changelog&quot;&gt;Polypane 26 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Small viewport emulation&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Safe area emulation&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; New iPhone and Android devices (thank Peter, Jules, Declan, Markus, Floris, James, Julian, Jeroen, Kari, Kriszti, Tim, Alex, Greg, Sara, Ryan, Pontus, Jan and Christian!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Add multiple measurement overlays at once&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Chromium 140&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Support inlining import rules (Thanks Nathan!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Updated CSS property and value suggestions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Styles update after pane resize (Thanks Bob!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: New attributes are recognised as idrefs&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: cmd + h toggles visibility of selected element (Thanks Harald!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Performance improvements for large CSS files&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: hidden elements are now more desaturated&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Improve rendering of style panel for Bricks based sites&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: inspect and edit :host styling (Thanks Murat!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Suggestions for landmarks and headers have a separate section now&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Astro and Nuxt devtoolbars are now ignored for outlines&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Clarification for the term &amp;#x27;label&amp;#x27; (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Show suggested extensions in the extension panel&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Show suggestions for extensions that have built-in alternatives&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Workspaces: new device lab, iOS and Android workspaces&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Workspaces: updated worldwide most used sizes workspace&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Permissions: Better handling of permissions on file protocol&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Sessions: option to show session name abovs panes (Thanks Kiran!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Tabs: new context menu option to reopen closed tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Screenshots: Always hide scrollbar when taking screenshots&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Navigation: You can now turn off navigation sync for an entire tab at once (Thanks Kiran!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Scroll sync now runs at higher framerates&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Color picker: export color and background combination as CSS&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Layout switcher: links to workspaces (Thanks David!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; App-wide confirmation system to prevent destructive actions (Thanks David!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Cache disabling logic now handles more cases&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Switch to new Chat support provider Crisp&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Better logic for overflow detection (Thanks Emma!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; updated list of Google fonts&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Prevent elements panel crash for certain HTML structures (Thanks Pandu!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Reference image: Top offset on reference image cuts off by viewport (Thanks Niels!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Rulers: issue where you couldn&amp;#x27;t add guides in full layout with measure overlay visible&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Breakpoints: Fix detection logic for media query ranges&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Breakpoints: Correctly detect all nested breakpoints (Thanks Erik!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Color picker: Colors on Wayland were always fully transparent (Thanks Philipp!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Outline panel: Deal with HTML element height affecting outline detection (Thanks Eric!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Focus sync didn&amp;#x27;t work in some cases&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Context menu: &amp;#x27;autofill form&amp;#x27; is now shown on all form descendants&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Issue where bulk screenshots sometimes wouldn&amp;#x27;t be saved&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[CSS-only floating focus with anchor positioning]]></title><description><![CDATA[The  Track focus debug tool  in Polypane shows a floating outline that follows the keyboard focus around the page. This makes it easier to…]]></description><link>https://polypane.app/blog/css-only-floating-focus-with-anchor-positioning/</link><guid isPermaLink="false">https://polypane.app/blog/css-only-floating-focus-with-anchor-positioning/</guid><pubDate>Tue, 16 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;a href=&quot;/docs/debug-tools/#track-focus&quot;&gt;Track focus debug tool&lt;/a&gt; in Polypane shows a floating outline that follows the keyboard focus around the page. This makes it easier to keep track of where the focus is and lets you determine (along with the focus overlay in Polypane) if the focus order makes sense.&lt;/p&gt;&lt;video src=&quot;static/track-focus-c02d6bc64e898bb181ef3352a82b6799.mp4&quot; controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; class=&quot;imgshadow&quot; style=&quot;margin:2rem auto;max-width:100%&quot;&gt;&lt;/video&gt;&lt;p&gt;Our debug tool uses JavaScript. With CSS Anchor positioning we can get the same effect without any JavaScript at all, so let&amp;#x27;s see how that works.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The Track focus debug tool is based on the &lt;a href=&quot;https://engineering.q42.nl/floating-focus/&quot;&gt;floating focus&lt;/a&gt; concept by Guido Bouman at &lt;a href=&quot;https://q42.nl/&quot;&gt;Q42&lt;/a&gt;. Be sure to read his article, which also includes accessibility research.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;why-css-only&quot;&gt;Why CSS-only?&lt;/h2&gt;&lt;p&gt;Our JavaScript implementation works well, but measuring and keeping track of elements on the JavaScript main thread is not ideal, since it takes the browser extra work and could interfere with the JS already on the page.&lt;/p&gt;&lt;p&gt;Using only CSS means all the work is offloaded to the browser&amp;#x27;s rendering engine, which can optimize it better. That means less CPU usage, better battery life and smoother animations.&lt;/p&gt;&lt;h2 id=&quot;css-anchor-positioning&quot;&gt;CSS Anchor positioning&lt;/h2&gt;&lt;p&gt;CSS Anchor positioning allows you to position elements relative to other elements on the page. It&amp;#x27;s supported in Chromium (Polypane included) and Safari as of version 26, provided the elements are keyboard focusable. Support in Firefox is underway.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;In Safari, button elements are not keyboard focusable by default. Users must enable this in Safari&amp;#x27;s settings under Advanced &amp;gt; &amp;quot;Press Tab to highlight each item on a webpage&amp;quot;. On your own pages, you can make elements keyboard focusable by adding a &lt;code class=&quot;language-text&quot;&gt;tabindex=&amp;quot;0&amp;quot;&lt;/code&gt; attribute to them. We have done so in the demo below, but be aware that users might not expect this behavior if they&amp;#x27;re used to Safari&amp;#x27;s default.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;This post isn&amp;#x27;t a tutorial on how to use CSS Anchor positioning, since there are quite a few parts of it that we don&amp;#x27;t need for our focus tracking. If you want to learn more about Anchor positioning, here are some great resources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ishadeed.com/article/anchor-positioning/&quot;&gt;The Basics of Anchor Positioning by Ahmad Shadeed&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/anchor-positioning-api&quot;&gt;Introducing the CSS anchor positioning API by Una Kravets for the Chrome Developers blog&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/one-of-those-onboarding-uis-with-anchor-positioning/&quot;&gt; One of Those “Onboarding” UIs, With Anchor Positioning by Ryan Trimble &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;setting-up-focus-tracking-with-css-anchor-positioning&quot;&gt;Setting up focus tracking with CSS Anchor positioning&lt;/h2&gt;&lt;p&gt;For CSS Anchor positioning, we need two things:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;An element that anchors itself.&lt;/li&gt;&lt;li&gt;An element that the first element is anchored to.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Linking these two element is done by setting an &lt;code class=&quot;language-text&quot;&gt;anchor-name&lt;/code&gt; on the element that can be anchored to, and referencing that in the &lt;code class=&quot;language-text&quot;&gt;position-anchor&lt;/code&gt; property on the element that does the anchoring.&lt;/p&gt;&lt;p&gt;If we want to have an indicator that follows the focus, we need to set the anchor-name on whatever element has the focus:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;*:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;anchor-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;#x27;s all the CSS we need for the element that can be anchored to.&lt;/p&gt;&lt;p&gt;Now we need to create the element that will follow the focus. In our JavaScript implementation we use a custom web component for this. For this CSS-only version, we&amp;#x27;re going to use the &lt;code class=&quot;language-text&quot;&gt;::before&lt;/code&gt; pseudo-element on the &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt;, so that we don&amp;#x27;t need to add any extra elements to the page:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This by itself doesn&amp;#x27;t do anything, because though we&amp;#x27;ve set and referenced an anchor, we haven&amp;#x27;t yet told it &lt;em&gt;how&lt;/em&gt; to anchor itself. For that, we use the inset properties with the new &lt;code class=&quot;language-text&quot;&gt;anchor()&lt;/code&gt; function:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inside&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;anchor()&lt;/code&gt; function returns positioning from the element that you&amp;#x27;re anchored to. The &lt;code class=&quot;language-text&quot;&gt;inside&lt;/code&gt; keyword is a shorthand that sets all four inset properties (&lt;code class=&quot;language-text&quot;&gt;top&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;right&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bottom&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;left&lt;/code&gt;) to the corresponding values from the anchored element.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s nice if the focus indicator is slightly bigger than the focused element, so it surrounds it without overlapping it.&lt;/p&gt;&lt;p&gt;While we can use the anchor function in a calc by setting &lt;code class=&quot;language-text&quot;&gt;top: calc(anchor(top) - 6px)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;right: calc(anchor(right) - 6px)&lt;/code&gt; and so on, it&amp;#x27;s easier to use a negative margin to achieve the same effect on all sides at once:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inside&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You still won&amp;#x27;t see anything, because we haven&amp;#x27;t given our focus indicator any style. Let&amp;#x27;s add some styling to make it visible:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inside&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff02&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px solid #3d80a2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2147483647&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inset 0.2s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gives our focus indicator a semi-transparent background and a border. The &lt;code class=&quot;language-text&quot;&gt;pointer-events: none&lt;/code&gt; ensures that it doesn&amp;#x27;t interfere with clicking or focusing elements on the page. The &lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; is set as high as possible to ensure it&amp;#x27;s on top of other elements. Lastly we add a transition to the &lt;code class=&quot;language-text&quot;&gt;inset&lt;/code&gt; property, which will animate the movement of the pseudo-element when the focus changes.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;2147483647&lt;/code&gt; for the z-index seems an arbitrary number, but it&amp;#x27;s actually the highest possible value for a 32-bit signed integer, which is what browsers use for z-index values.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;hiding-the-indicator-when-theres-no-focus&quot;&gt;Hiding the indicator when there&amp;#x27;s no focus&lt;/h2&gt;&lt;p&gt;When an element isn&amp;#x27;t anchored (in our case, when there are no focused elements), the element is unfortunately still visible because even though it has no width and height, it still has a border.&lt;/p&gt;&lt;p&gt;As of now there is no way to show anchor elements only when they&amp;#x27;re anchored, so we need to find another workaround.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.frontend.fyi/&quot;&gt;Jeroen&lt;/a&gt; suggested using &lt;code class=&quot;language-text&quot;&gt;:focus-within&lt;/code&gt;, since that will tell us if there is &lt;em&gt;any&lt;/em&gt; element that&amp;#x27;s focused. By combining it with &lt;code class=&quot;language-text&quot;&gt;:not()&lt;/code&gt;, we can select the &lt;code class=&quot;language-text&quot;&gt;::before&lt;/code&gt; pseudo-element when there are no focused elements and hide it:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html:not(:focus-within)::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&quot;hiding-the-default-focus-outline-progressively-enhanced&quot;&gt;Hiding the default focus outline, progressively enhanced&lt;/h2&gt;&lt;p&gt;Browsers by default give focused elements an outline, and it&amp;#x27;s part of your site that you can also implicitly design (by styling &lt;code class=&quot;language-text&quot;&gt;:focus-visible&lt;/code&gt; in a way that fits your design). Now that we built our own focus indicator, we want to hide that default outline.&lt;/p&gt;&lt;p&gt;Since CSS Anchor positioning isn&amp;#x27;t supported everywhere yet, we should make sure that we only replace that default outline with our floating one in browsers that have support. We can do that by only hiding the outline when &lt;code class=&quot;language-text&quot;&gt;position-anchor&lt;/code&gt; is supported:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;*:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And while we&amp;#x27;re at it, we can also wrap all the other CSS in that &lt;code class=&quot;language-text&quot;&gt;@supports&lt;/code&gt; rule, so that none of it is applied in browsers that don&amp;#x27;t support it. Thanks &lt;a href=&quot;https://krijnhoetmer.nl/&quot;&gt;Krijn&lt;/a&gt; for suggesting I add the progressive enhancement logic!&lt;/p&gt;&lt;p&gt;There is also a &lt;a href=&quot;https://anchor-positioning.oddbird.net/&quot;&gt;Anchor Positioning Polyfill&lt;/a&gt; available that you can include on your site to add support for browsers that don&amp;#x27;t support it natively yet.&lt;/p&gt;&lt;h2 id=&quot;result-and-demo&quot;&gt;Result and Demo&lt;/h2&gt;&lt;p&gt;And that&amp;#x27;s it! Here&amp;#x27;s all the CSS together:&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;*:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;anchor-name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;html::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x27;&amp;#x27;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;position-anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; --focus-anchor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inside&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff02&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px solid #3d80a2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 6px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2147483647&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inset 0.2s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;html:not(:focus-within)::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With just those few lines of CSS, we have a floating focus indicator that follows the keyboard focus around the page.&lt;/p&gt;&lt;p&gt;Try it out below (In Polypane, Chromium or Safari) by clicking one of the buttons and then using the Tab key to move the focus around:&lt;/p&gt;&lt;div class=&quot;focus-demo-container-wrap&quot;&gt;&lt;div class=&quot;focus-demo-container&quot;&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 1&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 2&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 3&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 4&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 5&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 6&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 7&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 8&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 9&lt;/button&gt;&lt;button tabindex=&quot;0&quot;&gt;Button 10&lt;/button&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Need a refresher on the rest of the anchor positioning syntax? Our &lt;a href=&quot;/cheatsheets/&quot;&gt;anchor positioning cheatsheet&lt;/a&gt; covers the properties and functions used here.&lt;/p&gt;&lt;p&gt;Or try it out on &lt;a href=&quot;https://codepen.io/Kilian/pen/ZYbgBxx&quot;&gt;CodePen&lt;/a&gt;:&lt;/p&gt;&lt;iframe height=&quot;300&quot; style=&quot;width:100%;max-width:100%&quot; scrolling=&quot;no&quot; title=&quot;css track focus&quot; src=&quot;https://codepen.io/Kilian/embed/ZYbgBxx?default-tab=css%2Cresult&quot; frameBorder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;&quot;&gt;See the Pen &lt;a href=&quot;https://codepen.io/Kilian/pen/ZYbgBxx&quot;&gt;css track focus&lt;/a&gt; by Kilian Valkhof (&lt;a href=&quot;https://codepen.io/Kilian&quot;&gt;@Kilian&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/iframe&gt;</content:encoded></item><item><title><![CDATA[Polypane with Stefan Judis]]></title><description><![CDATA[I recently joined  Stefan Judis  for  a Polypane walkthrough/demo  over on his YouTube channel. We touched on how I came to develop Polypane…]]></description><link>https://polypane.app/blog/polypane-with-stefan-judis/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-with-stefan-judis/</guid><pubDate>Mon, 18 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently joined &lt;a href=&quot;https://www.stefanjudis.com/&quot;&gt;Stefan Judis&lt;/a&gt; for &lt;a href=&quot;https://www.stefanjudis.com/notes/talking-about-the-polypane-browser-with-kilian-valkhof/&quot;&gt;a Polypane walkthrough/demo&lt;/a&gt; over on his YouTube channel.&lt;/p&gt;&lt;p&gt;We touched on how I came to develop Polypane and what it&amp;#x27;s like to be an &amp;#x27;indie developer&amp;#x27;, then we dived right in and explored all the ways Polypane can help you be more productive. Check the video out below!&lt;/p&gt;&lt;h2 id=&quot;recording&quot;&gt;Recording&lt;/h2&gt;&lt;div style=&quot;position:relative;width:100%;padding-top:56.25%&quot;&gt;&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/ThOq55qbJ2Y?si=PZohR_4__xcJwNPD&quot; title=&quot;YouTube video player&quot; frameBorder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot;position:absolute;top:0px;left:0px;width:100%;height:100%&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;h2 id=&quot;web-weekly&quot;&gt;Web Weekly&lt;/h2&gt;&lt;p&gt;Stefan runs an &lt;em&gt;excellent&lt;/em&gt; weekly newsletter for developers called &lt;a href=&quot;https://webweekly.email/&quot;&gt;Web Weekly&lt;/a&gt; that we highly recommend subscribing to!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Polypane 25.1: DRM support and bug fixes]]></title><description><![CDATA[Polypane 25.1 is now available, and it brings DRM support through Widevine, a new reopen last closed tab feature and a number of…]]></description><link>https://polypane.app/blog/polypane-25-1-drm-support-and-bug-fixes/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-25-1-drm-support-and-bug-fixes/</guid><pubDate>Wed, 30 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Polypane 25.1 is now available, and it brings DRM support through Widevine, a new reopen last closed tab feature and a number of improvements and fixes.&lt;/p&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introOuter--2Rw9m&quot;&gt;&lt;div class=&quot;PolypaneIntro-styles-module--introInner--1rBOU&quot;&gt;&lt;span class=&quot;PolypaneIntro-styles-module--icon--w0sRL &quot;&gt;&lt;img src=&quot;/img/brand/polypane-icon.svg&quot; alt=&quot;Polypane icon&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;PolypaneIntro-styles-module--text--2OpYk&quot;&gt;&lt;strong&gt;What&amp;#x27;s &lt;a href=&quot;/&quot;&gt;Polypane&lt;/a&gt;?&lt;/strong&gt; Polypane has everything a browser has, plus all the tools you need to build responsive, fast, accessible, and SEO-friendly websites and apps with multi-viewport synchronised previews, comprehensive device emulation and extensive accessibility tooling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 id=&quot;drm-support&quot;&gt;DRM support&lt;/h2&gt;&lt;p&gt;Polypane 25.1 adds support for DRM through Widevine, which means you can now watch and listen to DRM protected content in Polypane. It took a few starts and stalls to get Widevine integrated into Polypane properly without compromising on the security of Polypane, but it&amp;#x27;s here now.&lt;/p&gt;&lt;img src=&quot;static/netflix-drm-b6d720bcbb3133e0d7d7b31a2e0eedfd.jpg&quot; alt=&quot;Polypane playing a netflix DRM video&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;&lt;em&gt;That black rectangle when making a screenshot is the DRM protection in action.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;We want to thank the team at &lt;a href=&quot;https://castlabs.com/&quot;&gt;CastLabs&lt;/a&gt; for their responsiveness and willingness to change their build process to get this working, as well as &lt;a href=&quot;https://github.com/mmaietta&quot;&gt;Mike Maietta&lt;/a&gt; for his help integrating the required features into our build system.&lt;/p&gt;&lt;h2 id=&quot;reopen-last-closed-tab&quot;&gt;Reopen last closed tab&lt;/h2&gt;&lt;p&gt;You can now re-open closed tabs with &lt;kbd class=&quot;ShortcutDisplay-styles-module--shortcutdisplay--318pA&quot;&gt;⇧ ⌘ T&lt;/kbd&gt;.&lt;/p&gt;&lt;p&gt;It won&amp;#x27;t help you build better websites, but it should save you some time when you accidentally close a tab you didn&amp;#x27;t mean to close. It&amp;#x27;s a small feature, but one that we hope will make your life just a little bit easier.&lt;/p&gt;&lt;h2 id=&quot;show-only-links-with-issues&quot;&gt;Show only Links with issues&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/outline-panel/#links&quot;&gt;links outline&lt;/a&gt; now has an option to show only broken links so you can focus on just the links that have issues. This is especially useful for large pages with many links, as it allows you to quickly identify and fix broken or problematic links without having to scroll through all the working ones.&lt;/p&gt;&lt;img src=&quot;static/linkissues-61f0c817a3b380b4eca11630b80986f2.png&quot; alt=&quot;Polypane showing only links with issues on this page&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;form-autofilling-improvements&quot;&gt;Form autofilling improvements&lt;/h2&gt;&lt;p&gt;Our &lt;a href=&quot;/docs/form-autofill/&quot;&gt;form autofilling&lt;/a&gt; feature makes it really easy to quickly fill out an entire form with realistic data, but it also filled in &lt;code class=&quot;language-text&quot;&gt;hidden&lt;/code&gt; inputs. Since those are often used to communicate extra data to the server, this could cause issues.&lt;/p&gt;&lt;p&gt;Starting from 25.1 we leave those hidden inputs alone, which should make form autofilling more reliable. Thanks Mathieu for the request!&lt;/p&gt;&lt;h2 id=&quot;elements-panel-fixes&quot;&gt;Elements panel fixes&lt;/h2&gt;&lt;p&gt;We added support for comments in our &lt;a href=&quot;/blog/polypane-25-browser-extensions-with-manifest-v-3-form-outlines-and-chromium-138/#polypane-2501-changelog&quot;&gt;25.0.1 release&lt;/a&gt;, but in doing so accidentally broke support for inspecting web component internals in the Elements panel. This has been fixed, so you can now inspect shadow DOMs again. Thanks Mark for making us aware of this!&lt;/p&gt;&lt;img src=&quot;static/webcomponents-5367c74cfab3a30e48c27495c902f6b0.png&quot; alt=&quot;Elements panel with an opened shadowRoot&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;We&amp;#x27;ve also fixed an issue where some CSS wouldn&amp;#x27;t be shown because we couldn&amp;#x27;t parse the oklch color space correctly. Later this year we will roll out even more wide color space support across the app.&lt;/p&gt;&lt;h2 id=&quot;meta-panel-improvements&quot;&gt;Meta panel improvements&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;/docs/meta-information/&quot;&gt;meta panel&lt;/a&gt; now warns you when a robots meta tag prevents indexing. While we already &lt;a href=&quot;/docs/meta-information/#robotstxt&quot;&gt;tested against your &lt;code class=&quot;language-text&quot;&gt;robots.txt&lt;/code&gt;&lt;/a&gt;, we now also test against the contents of the meta tag. Thanks Laurent for the suggestion!&lt;/p&gt;&lt;img src=&quot;static/robotsmeta-644f236379703a96c2a492e1cd3a0d94.png&quot; alt=&quot;Meta panel showing a robots meta tag that prevents indexing&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;We&amp;#x27;ve also solved various bugs that occurred for some people like the app freezing when trying to &lt;a href=&quot;/docs/meta-information/#hiding-and-sorting-the-social-card-previews&quot;&gt;hide social media previews&lt;/a&gt; (Thanks George!), more robust &lt;a href=&quot;/docs/meta-information/#structured-data&quot;&gt;testing of structured data&lt;/a&gt; (Thanks Laurent!) and a fix for microdata not always being shown despite being detected.&lt;/p&gt;&lt;h2 id=&quot;get-polypane-251&quot;&gt;Get Polypane 25.1&lt;/h2&gt;&lt;p&gt;Polypane is available for Windows, Mac and Linux in 64 bit and ARM versions.&lt;/p&gt;&lt;p&gt;Polypane automatically updates on Mac, Windows and on Linux when using the AppImage. Otherwise, go to &lt;a href=&quot;/download/&quot;&gt;the download page&lt;/a&gt; to download the latest version!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Don&amp;#x27;t have Polypane yet? There is a 14 day trial available. &lt;br/&gt; &lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try it for free&lt;/a&gt;.&lt;/strong&gt; No credit card needed.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 id=&quot;polypane-2511-changelog&quot;&gt;Polypane 25.1.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Prevent permission popups for DRM requests&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; DRM: Fix Spotify playback on Mac&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Color contrast now takes color-scheme settings into account (Thanks Karthik!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: using arrow keys while editing attributes could result in the attribute being deleted&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Fix issue where shadow root couldn&amp;#x27;t be selected (Thanks Mark!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Show child nodes for elements that only have a shadowRoot as direct child&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;polypane-251-changelog&quot;&gt;Polypane 25.1 Changelog&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;New&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; DRM support through Widevine&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New&lt;/strong&gt; Reopen last closed tab feature&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Outline panel: Show only links with issues option (Thanks Anthony!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Form outline: prevent form element rows from being too tall&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Meta panel: Shows warning when a robots meta tag prevents indexing (Thanks Laurent!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Guides: Split view layout now has enough space for the vertical ruler&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Form autofilling no longer changes hidden fields (Thanks Mathieu!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Prevent generated screenshot names from becoming too long&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Polypane Peek: hide contrast value if no contrast is available&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Elements panel: Expand elements in the tree view if they have text content and pseudo elements (Thanks Laurent!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Extensions: Find extensions now opens in new tab&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Quick Start: shortcuts now no longer change when emulating different operating systems (Thanks Damien!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated list of Google Fonts&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Improved&lt;/strong&gt; Updated Chromium to 138.0.7204.157&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Fixes&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements panel: Show shadowRoot contents again (Thanks Mark!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Elements Panel: parsing of oklab/oklch text-shadows in elements panel and tooltips&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Guides: NaN values in guides for vertical ruler in split layout&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Closing popups in focus and full mode reopens devtools again (thanks Karl!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Adjusted panel text size was only applied in the first tab (Thanks Laurent!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: catch errors when parsing faulty microdata (Thanks Laurent!)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: Now show microdata when available on its own rather than together with RDFa&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Fix&lt;/strong&gt; Meta panel: App freeze when trying to hide social media previews (Thanks George!)&lt;/li&gt;&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Polypane on the Winging It Live stream]]></title><description><![CDATA[Last week I joined Miriam Suzanne and James Stuckey Weber on the Winging It Live stream to talk about tools for developers, and Polypane in…]]></description><link>https://polypane.app/blog/polypane-on-the-winging-it-live-stream/</link><guid isPermaLink="false">https://polypane.app/blog/polypane-on-the-winging-it-live-stream/</guid><pubDate>Fri, 25 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week I joined Miriam Suzanne and James Stuckey Weber on the Winging It Live stream to talk about tools for developers, and Polypane in particular.&lt;/p&gt;&lt;p&gt;We had a great time discussing the challenges of developing tools for developers, compared notes on &lt;a href=&quot;https://colorcontrast.app&quot;&gt;contrast checkers&lt;/a&gt; and how it&amp;#x27;s a pain to work on things you don&amp;#x27;t get to use yourself, like tools in Polypane not working on Polypane, or writing specs.&lt;/p&gt;&lt;p&gt;There might have been a rant or two as well.&lt;/p&gt;&lt;div style=&quot;position:relative;width:100%;padding-top:56.25%&quot;&gt;&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/ou6ViXTflXw?si=HNRFz7024lRyUNBL&quot; title=&quot;YouTube video player&quot; frameBorder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot;position:absolute;top:0px;left:0px;width:100%;height:100%&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;p&gt;Thanks James and Miriam for having me on!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[HTML Form Inspector: inspect and debug your forms online]]></title><description><![CDATA[HTML forms are notoriously hard to get right. They require a lot of boilerplate HTML and even small mistakes can lead to frustrating user…]]></description><link>https://polypane.app/blog/html-form-inspector-inspect-and-debug-your-forms-online/</link><guid isPermaLink="false">https://polypane.app/blog/html-form-inspector-inspect-and-debug-your-forms-online/</guid><pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;HTML forms are notoriously hard to get right. They require a lot of boilerplate HTML and even small mistakes can lead to frustrating user experiences like not being able to click a label to toggle a checkbox.&lt;/p&gt;&lt;h2 id=&quot;finding-issues-in-forms&quot;&gt;Finding issues in forms&lt;/h2&gt;&lt;p&gt;Because forms are so verbose, it can also be hard to discover issues in them just by looking at the code. For example, how long does it take you to spot the problem in this form?&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;E-mail&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Password&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;current-password&lt;span class=&quot;token punctuation&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;Sign in&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you think it&amp;#x27;s the empty &lt;code class=&quot;language-text&quot;&gt;action&lt;/code&gt; attribute, you&amp;#x27;re wrong: an empty action attribute will submit the form to the current page.&lt;/p&gt;&lt;p&gt;If you think it&amp;#x27;s the button without a &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;submit&lt;/code&gt;, that&amp;#x27;s also fine: if there&amp;#x27;s a single button in a form, it will be treated as a submit button by default.&lt;/p&gt;&lt;p&gt;In this case, our password input is missing an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; attribute, which means that the label above it is not associated with the input. This means the input has no accessible name, and clicking the label will not focus the input.&lt;/p&gt;&lt;p&gt;It&amp;#x27;s easy to overlook because as you can see, the line with the input contains the word &amp;quot;password&amp;quot; &lt;strong&gt;three times&lt;/strong&gt;. How much more does a browser need to tell you that this input is a password?&lt;/p&gt;&lt;p&gt;Now take a look at the following image. How long does it take you to spot the problem?&lt;/p&gt;&lt;img src=&quot;static/errorform-507dedfec9e76b5e5ecc6b23d6598087.png&quot; alt=&quot;Three rectangles listing the form configuration, the fields where the input with type password has no label, and a group listing the button with a type of submit.&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;That&amp;#x27;s much quicker, right? You instantly see that the password input has no label, and now you can fix it.&lt;/p&gt;&lt;h2 id=&quot;introducing-the-html-form-inspector&quot;&gt;Introducing the HTML Form Inspector&lt;/h2&gt;&lt;p&gt;The screenshot above is how the form above is shown in our new &lt;a href=&quot;/form-inspector/&quot;&gt;HTML Form Inspector&lt;/a&gt;, a free online tool that helps you inspect and debug your HTML forms.&lt;/p&gt;&lt;p&gt;As we were building out the &lt;a href=&quot;/docs/outline-panel/#forms&quot;&gt;Forms Outline&lt;/a&gt; feature in Polypane and subsequently finding out that &lt;em&gt;all our forms&lt;/em&gt; had issues easily picked up, we realized that this would be a great tool to have online.&lt;/p&gt;&lt;p&gt;So we took the core functionality of our Forms Outline, retooled it from using the Live DOM (which we use in Polypane) to using the HTML source code, and made it &lt;a href=&quot;/form-inspector/&quot;&gt;available online for everyone to use&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;how-does-it-work&quot;&gt;How does it work?&lt;/h2&gt;&lt;p&gt;The HTML Form Inspector takes the HTML source code of your form and shows it in a structured way, splitting out the various parts of the form:&lt;/p&gt;&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;&lt;p&gt;The inspector starts with a summary, so can at a glance see how many fields in total, how many required fields, how many fieldsets and how many buttons there are in the form. This already helps you spot issues like a missing &lt;code class=&quot;language-text&quot;&gt;required&lt;/code&gt; attribute or a missing button.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; alt=&quot;4 numbers showing the total fields, required fields, fieldsets and buttons.&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;form-configuration&quot;&gt;Form configuration&lt;/h3&gt;&lt;p&gt;The form configuration shows the &lt;code class=&quot;language-text&quot;&gt;action&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;method&lt;/code&gt; attributes of the form so you know where it submits to and how it submits.&lt;/p&gt;&lt;img src=&quot;data:image/png;base64,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&quot; alt=&quot;Form configuration overview showing an action and method&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;fields&quot;&gt;Fields&lt;/h3&gt;&lt;p&gt;The fields section lists all the fields in the form that aren&amp;#x27;t part of a fieldset and shows their element, type, name, label, value/checked status and whether they are required and disabled. You can click open the details of each field to see any other attributes like &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;autocomplete&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;placeholder&lt;/code&gt; and more.&lt;/p&gt;&lt;img src=&quot;static/fields-683009cbb7e3f6afc931efa5bd8a53de.png&quot; alt=&quot;Field overview showing a table of fields and their properties&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;fieldsets&quot;&gt;Fieldsets&lt;/h3&gt;&lt;p&gt;Each fieldset in the form is shown in its own section, with the &lt;code class=&quot;language-text&quot;&gt;legend&lt;/code&gt; and all the fields that are part of that fieldset. This makes it easy to see which fields are grouped together and under what title.&lt;/p&gt;&lt;img src=&quot;static/fieldset-0a734d90d059e011728b2e43195bfdd0.png&quot; alt=&quot;Fieldet overview showing the fieldset name and a table of fields and their properties&quot; class=&quot;imgshadow&quot;/&gt;&lt;h3 id=&quot;buttons&quot;&gt;Buttons&lt;/h3&gt;&lt;p&gt;The last section lists all the buttons in the form, showing their type, name, value and whether they are disabled. This is useful to see if you have a submit button and if the label makes sense.&lt;/p&gt;&lt;img src=&quot;static/buttons-e98267eb13be813c2b6b53efd62213a9.png&quot; alt=&quot;Button overview showing a table of buttons and their properties&quot; class=&quot;imgshadow&quot;/&gt;&lt;h2 id=&quot;try-it-yourself&quot;&gt;Try it yourself!&lt;/h2&gt;&lt;p&gt;You can find the HTML Form Inspector at &lt;a href=&quot;/form-inspector/&quot;&gt;polypane.app/form-inspector&lt;/a&gt;, or click the image below:&lt;/p&gt;&lt;a href=&quot;/form-inspector/&quot; style=&quot;display:block;max-width:initial;border-radius:4px&quot;&gt;&lt;img style=&quot;display:block&quot; src=&quot;static/forminspector-c3a87718955d63bdade6852031c79e8c.png&quot; alt=&quot;The HTML Form Inspector&quot; class=&quot;imgshadow&quot;/&gt;&lt;/a&gt;&lt;p&gt;We know that making this tool freely available might not be the smartest business move, but we couldn&amp;#x27;t find anything else like it out there.&lt;/p&gt;&lt;p&gt;And honestly, we&amp;#x27;ve discovered so many forms with easily detectable issues that we felt compelled to share it with everyone.&lt;/p&gt;&lt;h2 id=&quot;go-even-deeper-with-polypane&quot;&gt;Go even deeper with Polypane&lt;/h2&gt;&lt;p&gt;In Polypane we use the live DOM to check forms which means we can pick up even more issues but also provide you with more context, and suggestions on how to improve your forms.&lt;/p&gt;&lt;p&gt;For example, we can tell you if the &lt;code class=&quot;language-text&quot;&gt;autocomplete&lt;/code&gt; attribute has an invalid value, when the label is set by something other than a label (like ARIA, or a placeholder), if your radio groups are structured correctly, and so much more.&lt;/p&gt;&lt;p&gt;When you&amp;#x27;ve tested your forms with the HTML Form Inspector, you can use Polypane to go even deeper and fix any remaining issues.&lt;/p&gt;&lt;img src=&quot;static/formcta-a87dcc0f6321b966c97e4fd270248624.png&quot; alt=&quot;Form Outline in Polypane showing missing labels&quot; class=&quot;imgshadow&quot;/&gt;&lt;p&gt;Polypane has a free 14 day trial (no credit card needed) that will let you test all your forms, along with dozens of other parts of your sites: &lt;strong&gt;&lt;a href=&quot;https://dashboard.polypane.app/register&quot;&gt;Try Polypane for free&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;</content:encoded></item></channel></rss>