<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>bsmth.de</title><description>Product manager at Mozilla working on Firefox Enterprise.</description><link>https://bsmth.de/</link><language>en</language><lastBuildDate>Mon, 19 Oct 2026 20:14:00 GMT</lastBuildDate><atom:link href="https://bsmth.de/rss.xml" rel="self" type="application/rss+xml"/><item><title>Firefox Enterprise is here</title><link>https://bsmth.de/firefox-enterprise/</link><guid isPermaLink="true">https://bsmth.de/firefox-enterprise/</guid><description>Mozilla has launched Firefox Enterprise, a browser and self-hosted Admin Console for organizations. I&apos;ve been working on it as a product manager since 2025.</description><pubDate>Tue, 13 Oct 2026 08:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, &lt;a href=&quot;https://blog.mozilla.org/en/firefox/introducing-firefox-enterprise/&quot;&gt;Mozilla launched Firefox Enterprise&lt;/a&gt;.
The first release is &lt;a href=&quot;https://www.firefox.com/en-US/firefox/enterprise/158.0/releasenotes/&quot;&gt;Firefox Enterprise 158&lt;/a&gt;, which branches off Firefox 158.
I’ve been working on it as a product manager since 2025, so it’s good to finally be able to talk about what we’ve been building.
This post covers how I ended up working on it, and the decisions I pushed for along the way.&lt;/p&gt;
&lt;h2 id=&quot;from-mdn-to-firefox&quot;&gt;From MDN to Firefox&lt;/h2&gt;
&lt;p&gt;I joined Mozilla in 2022 as a technical writer on &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN Web Docs&lt;/a&gt;.
I wrote a lot of HTTP documentation and the Firefox release notes for developers, but the job was wider than writing.
I ran content migrations, maintained &lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;browser compatibility data&lt;/a&gt;, worked with the communities who translate MDN, and helped keep one of the most active projects on GitHub running smoothly.&lt;/p&gt;
&lt;p&gt;In 2025, I moved from documenting Firefox to working on it.
It turned out writing and reading browser release notes for a few years also gave me a solid understanding of how a browser is built and shipped.&lt;/p&gt;
&lt;p&gt;Working on Enterprise means understanding how the whole product fits together: the UI, profile management, networking, preferences, enterprise policies, build and release systems, supporting services, testing infrastructure, and general operational constraints.&lt;/p&gt;
&lt;h2 id=&quot;what-is-firefox-enterprise&quot;&gt;What is Firefox Enterprise?&lt;/h2&gt;
&lt;p&gt;Firefox has been used in organizations for a long time.
The &lt;a href=&quot;https://support.mozilla.org/en-US/kb/firefox-esr-release-cycle&quot;&gt;Extended Support Release&lt;/a&gt; has been around since 2012, and the &lt;a href=&quot;https://support.mozilla.org/en-US/kb/enforce-policies-on-firefox-for-enterprise&quot;&gt;policy engine&lt;/a&gt; (mostly thanks to &lt;a href=&quot;https://mike.kaply.com/&quot;&gt;Mike Kaply&lt;/a&gt;) since &lt;a href=&quot;https://www.firefox.com/en-US/firefox/60.0/releasenotes/&quot;&gt;version 60 of Firefox&lt;/a&gt;.
Thousands of organizations deploy and manage Firefox with ESR and enterprise policies.&lt;/p&gt;
&lt;p&gt;Firefox Enterprise builds on that.
There are two parts: a Firefox browser with additional security features built for managed environments, and an Admin Console that you run on your own infrastructure.
IT teams use the Admin Console to manage policies and access, and to set controls for how data moves through the browser and how it can be used.&lt;/p&gt;
&lt;p&gt;The services Mozilla runs for consumer Firefox can be self-hosted alongside the Admin Console: browser updates, Sync (for history and bookmarks, for example), Remote Settings, telemetry, and crash collection.
Security event logging is off by default, and crash reporting can be turned off.
When enabled, security events, crash reports, and telemetry go to the customer’s Admin Console, not to Mozilla.&lt;/p&gt;
&lt;h2 id=&quot;what-we-landed-in-158&quot;&gt;What we landed in 158&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.firefox.com/en-US/firefox/enterprise/158.0/releasenotes/&quot;&gt;release notes&lt;/a&gt; have the full list, but here are some highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Company sign-in:&lt;/strong&gt; users must authenticate through their company’s single sign-on (SSO) before browsing, using any OpenID Connect (OIDC) identity provider.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device posture:&lt;/strong&gt; admins can require things like disk encryption or endpoint security for sign-in, and Firefox signs the user out if the device isn’t compliant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watermarking:&lt;/strong&gt; admins can overlay text such as the user’s email address and a timestamp on the sites they choose, so a leaked screenshot or photo of a sensitive page is attributable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data loss prevention (DLP):&lt;/strong&gt; admins can block or warn on paste, upload, download, and print on the sites they choose.
The built-in checks run on-device.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lockdown Mode:&lt;/strong&gt; admins can apply extra restrictions to specific sites, such as turning off JIT or isolating the site in its own container.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Access Connector:&lt;/strong&gt; users can access internal sites from a personal device without a device-wide VPN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Central policies:&lt;/strong&gt; admins manage policies from one place, and many changes apply without restarting the browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;building-firefox-enterprise&quot;&gt;Building Firefox Enterprise&lt;/h2&gt;
&lt;p&gt;We needed to know that Firefox Enterprise worked in real environments, and to find the gaps, before customers depended on it.
I helped structure that as an internal alpha first, followed by a customer pilot.
Working directly with prospective customers showed us what deploying and supporting a browser in an organization involves.
I also worked across the browser, Admin Console, infrastructure, and QA teams to manage dependencies and make scope trade-offs.&lt;/p&gt;
&lt;p&gt;Here are two product decisions I pushed for.&lt;/p&gt;
&lt;h3 id=&quot;building-dlp-into-the-browser&quot;&gt;Building DLP into the browser&lt;/h3&gt;
&lt;p&gt;For data loss prevention, the usual options are to buy a solution or partner with an existing vendor.
I pushed for another option: building it into Firefox Enterprise ourselves.
It was the harder path, but I think it was the right bet for two reasons.&lt;/p&gt;
&lt;p&gt;First, checks and enforcement happen client-side.
The browser evaluates the rules locally rather than sending content to a separate service to decide whether a paste, upload, download, or print is allowed, and DLP works without another agent to deploy.
If you already run a DLP agent, you can connect it through Firefox’s existing Content Analysis integration, and Firefox defers to it and turns the built-in checks off.&lt;/p&gt;
&lt;p&gt;Second, we keep control over what to build next.
Owning DLP end-to-end means our roadmap isn’t blocked by anyone else’s.&lt;/p&gt;
&lt;p&gt;Big thanks to &lt;a href=&quot;https://gregstoll.com/&quot;&gt;Greg Stoll&lt;/a&gt; and &lt;a href=&quot;https://bugzilla.mozilla.org/user_profile?user_id=506442&quot;&gt;David Parks&lt;/a&gt;, who built the original Content Analysis integration that lets people hook Firefox up to third-party DLP agents.&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;
Greg brought his experience with the policy surface and the browser internals to the Firefox Enterprise team and helped guide our implementation.
Thanks also to &lt;a href=&quot;https://www.linkedin.com/in/jmendezrice&quot;&gt;Jonathan Mendez&lt;/a&gt; for his help designing and implementing the new built-in agent.&lt;/p&gt;
&lt;h3 id=&quot;describing-policies-with-schema-metadata&quot;&gt;Describing policies with schema metadata&lt;/h3&gt;
&lt;p&gt;With more than 130 policies in the console, we needed something better than a text entry field for hand-written JSON, and designing UX for each policy doesn’t scale.&lt;/p&gt;
&lt;p&gt;We already had &lt;a href=&quot;https://github.com/mozilla-firefox/firefox/blob/main/browser/components/enterprisepolicies/schemas/policies-schema.json&quot;&gt;a schema&lt;/a&gt; that describes policies, and my proposal was to make it the single source of truth describing policy capabilities, with enough information in it for multiple types of consumer.
Practically, that meant adding richer data, including compatibility data, and using the schema to power a visual, form-based editor in the Admin Console.&lt;/p&gt;
&lt;p&gt;The console reads the schema and renders the appropriate fields and validation, without anyone having to build a separate UI for each policy.
Where a generated form needs extra care, we can override it with a custom editor.&lt;/p&gt;
&lt;p&gt;Here’s an excerpt of the schema entry for the &lt;code&gt;AIControls&lt;/code&gt; policy:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;jsonc&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;AIControls&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;object&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;x-category&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Content settings&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;x-compatibility&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;firefox&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;version_added&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;149.0.2&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;firefox_esr&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;version_added&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;153&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;firefox_enterprise&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;version_added&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;149.0.2&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // …&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One schema covers all of those policies today, and a new policy gets a basic form by extending the schema rather than designing a new screen.
People say the documentation is the product.
In our case, the metadata is the product: like browser compatibility data, it also powers downstream documentation and enriches other products.&lt;/p&gt;
&lt;h2 id=&quot;who-can-run-firefox-enterprise&quot;&gt;Who can run Firefox Enterprise&lt;/h2&gt;
&lt;p&gt;Firefox Enterprise is rolling out across Europe, with sales focused on France, Germany, Austria, and Switzerland to begin with.
More markets will follow.
You need an Admin Console to use it, so the way in is to &lt;a href=&quot;https://www.firefox.com/browsers/enterprise/&quot;&gt;request a demo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;more-to-come&quot;&gt;More to come&lt;/h2&gt;
&lt;p&gt;Getting a first public release out is only the start of the work, and I’ll write more about the details as we go.
If you run Firefox in your organization, or you’d like to and something is stopping you, I’d like to hear about it.
You can &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; or &lt;a href=&quot;mailto:contact@bsmth.de&quot;&gt;mail me&lt;/a&gt;.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;Updated October 19, 2026: Originally, I only credited Greg for the Content Analysis integration. David co-built it, so I’ve corrected that. &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded><category>Firefox</category><category>Mozilla</category><category>Firefox Enterprise</category></item><item><title>PSA: Cache your prettier writes</title><link>https://bsmth.de/caching-prettier-writes/</link><guid isPermaLink="true">https://bsmth.de/caching-prettier-writes/</guid><description>Since Prettier version 2.7, there&apos;s a --cache option for faster formatting</description><pubDate>Tue, 22 Jul 2025 07:11:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’re using Prettier in your projects, you might benefit from caching that’s been introduced since version 2.7 (June 2022).
Let’s have a look at how it works and how much faster it runs.&lt;/p&gt;
&lt;h2 id=&quot;caching-using-the-prettier-cli&quot;&gt;Caching using the Prettier CLI&lt;/h2&gt;
&lt;p&gt;The functionality was added by &lt;a href=&quot;https://github.com/sosukesuzuki&quot;&gt;@sosukesuzuki&lt;/a&gt; in &lt;a href=&quot;https://github.com/prettier/prettier/pull/12800&quot;&gt;prettier#12800&lt;/a&gt;, and it adds a &lt;code&gt;--cache&lt;/code&gt; option:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; ./files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use &lt;code&gt;-w&lt;/code&gt; as a shorthand for &lt;code&gt;--write&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# shorthand&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; -w&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; ./files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Watch out:&lt;/strong&gt; &lt;code&gt;-c&lt;/code&gt; is short for &lt;code&gt;--check&lt;/code&gt;, not &lt;code&gt;--cache&lt;/code&gt;.
Combining &lt;code&gt;-c&lt;/code&gt; and &lt;code&gt;-w&lt;/code&gt; doesn’t make sense (“check” and “write”), so everything under &lt;code&gt;./files&lt;/code&gt; will be formatted and a cache will be evicted / deleted:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# This means `--write --check`, so DON&apos;T DO THIS:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; -w&lt;/span&gt;&lt;span&gt; -c&lt;/span&gt;&lt;span&gt; ./files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a good illustration why verbose CLI options are better, especially in collaborative projects where someone else may have to guess what the shorthand means.&lt;/p&gt;
&lt;h2 id=&quot;prettiers-cache-location&quot;&gt;Prettier’s cache location&lt;/h2&gt;
&lt;p&gt;When you run Prettier with the &lt;code&gt;--cache&lt;/code&gt; option, a cache (a minified JSON file) is created at &lt;code&gt;./node_modules/.cache/prettier/.prettier-cache&lt;/code&gt;, which contains details about files that have been formatted.
You can also set the location to somewhere else if you want:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; --cache-location=.my-prettier-cache&lt;/span&gt;&lt;span&gt; ./files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can evict the cache by removing the file or running Prettier without the &lt;code&gt;--cache&lt;/code&gt; option:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; ./node_modules/.cache/prettier/.prettier-cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# or&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; ./files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;caching-strategies&quot;&gt;Caching strategies&lt;/h2&gt;
&lt;p&gt;There’s two options for the caching strategy; &lt;code&gt;content&lt;/code&gt; (the file contents) or &lt;code&gt;metadata&lt;/code&gt;, which I believe is the file’s last modified timestamp and size.
Metadata may change without file contents being changed, so caching on file &lt;strong&gt;contents&lt;/strong&gt; could be slower on first run due to reading data from disk, but avoids checking and formatting files with metadata-only changes.&lt;/p&gt;
&lt;p&gt;This appears to be inspired the &lt;a href=&quot;https://eslint.org/docs/latest/use/command-line-interface#--cache-strategy&quot;&gt;ESLint caching implementation&lt;/a&gt;.
Thanks to the open nature of the project’s development, it’s apparent that there’s &lt;a href=&quot;https://github.com/prettier/prettier/pull/12800#discussion_r889602368&quot;&gt;minimal real-world differences&lt;/a&gt; in those two strategies based on cherry-picked example sources.&lt;/p&gt;
&lt;p&gt;Don’t sweat the difference if you’re only interested in performance.
If you’re running Prettier in CI with caching enabled, you want to cache based on &lt;code&gt;content&lt;/code&gt; as you’re likely formatting a fresh clone whose ‘last modified’ timestamps are irrelevant.&lt;/p&gt;
&lt;h2 id=&quot;performance-impact-on-14000-files&quot;&gt;Performance impact on 14,000 files&lt;/h2&gt;
&lt;p&gt;I wrote about this before; this is the outcome of enabling Prettier’s cache in &lt;code&gt;mdn/content&lt;/code&gt;.
Running &lt;code&gt;prettier --write&lt;/code&gt; v &lt;code&gt;prettier --write --cache&lt;/code&gt; on 14,000 files comes out like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# without a cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;  98.97s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 10.11s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 126%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 1:26.14&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# with a cache&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;  14.82s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 7.73s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 129%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 17.396&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So in this case, we go from 90 seconds to 17 seconds, which definitely helps.
And yes, I do format the whole repo occasionally when testing config changes, applying updates, and other weird scenarios.&lt;/p&gt;
&lt;p&gt;Most authors aren’t formatting the whole repo locally very often, with pre-commit hooks running on &lt;strong&gt;staged files&lt;/strong&gt; only.
There’s still a lot of linting and formatting tools running during regular development due to search-and-replace changes and bulk edits, for instance.&lt;/p&gt;
&lt;h2 id=&quot;send-it-to-devnull&quot;&gt;Send it to /dev/null&lt;/h2&gt;
&lt;p&gt;Also it looks like you can shave off a precious second by sending everything to &lt;code&gt;/dev/null&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt; /dev/null&lt;/span&gt;&lt;span&gt;  14.74s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 7.29s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 134%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 16.424&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or by disabling logging:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;prettier&lt;/span&gt;&lt;span&gt; --write&lt;/span&gt;&lt;span&gt; --cache&lt;/span&gt;&lt;span&gt; --log-level=silent&lt;/span&gt;&lt;span&gt; .&lt;/span&gt;&lt;span&gt;  14.65s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 7.57s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 135%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 16.428&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;job-well-done&quot;&gt;Job well done&lt;/h2&gt;
&lt;p&gt;Well done to the author and the maintainers on the improvements.
GitHub says there’s &lt;a href=&quot;https://github.com/prettier/prettier/network/dependents&quot;&gt;9.9 million projects using Prettier&lt;/a&gt;, and the number is likely way higher, so I think there’s a serious chunk of developer years saved and hours of CI avoided through good old caching.
Congrats :clap:, and happy formatting!&lt;/p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;&lt;code&gt;mdn/browser-compat-data&lt;/code&gt;&lt;/a&gt; on GitHub&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>JavaScript</category><category>MDN</category></item><item><title>Using BCD to spot missing MDN Web Docs gaps</title><link>https://bsmth.de/bcd-missing-pages/</link><guid isPermaLink="true">https://bsmth.de/bcd-missing-pages/</guid><description>I made a demo project listing missing MDN Web Docs pages for BCD features</description><pubDate>Thu, 17 Jul 2025 19:13:00 GMT</pubDate><content:encoded>&lt;p&gt;I built a small proof-of-concept page that lists BCD features without MDN Web Docs pages.
I deployed it at &lt;a href=&quot;https://bcd.bsmth.de/&quot;&gt;bcd.bsmth.de/&lt;/a&gt; so you can have a look and see if you want to fill gaps in web documentation with us.
It’s far from stable and may change at any point, but this post describes how it works and why I built it.&lt;/p&gt;
&lt;h2 id=&quot;what-is-bcd&quot;&gt;What is BCD?&lt;/h2&gt;
&lt;p&gt;MDN Web Docs reference pages have tables at the bottom that show Browser Compatibility Data (BCD) for technologies like HTML, CSS, JavaScript, and others.
Previously, this data was maintained in pure HTML tables at the bottom of each MDN article, right after page content.&lt;/p&gt;
&lt;p&gt;Around July 2017, most of this hand-curated information was &lt;a href=&quot;https://github.com/mdn/browser-compat-data/issues/280&quot;&gt;converted from HTML to JSON&lt;/a&gt;, decoupling this support information from documentation.
The browser compat information landed in a repo called &lt;code&gt;mdn/browser-compat-data&lt;/code&gt;, which made maintenance a lot smoother and usability a lot more flexible.
To make this data available in projects programmatically, a Node.js package is built from the &lt;code&gt;browser-compat-data&lt;/code&gt; repository and published to npm.&lt;/p&gt;
&lt;p&gt;Data from &lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;&lt;code&gt;mdn/browser-compat-data&lt;/code&gt;&lt;/a&gt; powers a lot of tools, such as compatibility hints in your IDE, on Can I Use, powering Baseline, and many other tools for web development.&lt;/p&gt;
&lt;h2 id=&quot;using-bcd-in-a-project&quot;&gt;Using BCD in a project&lt;/h2&gt;
&lt;p&gt;I’ve contributed &lt;a href=&quot;https://github.com/mdn/browser-compat-data/pulls?q=is%3Apr+author%3Absmth+is%3Aclosed&quot;&gt;around 70 pull requests to BCD&lt;/a&gt; so far, most of which are about HTTP features and other technologies I touch when documenting release notes for Firefox.
What I also like about BCD is that you can pull it into your projects from npm:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; @mdn/browser-compat-data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can find out browser support for features like &lt;code&gt;Temporal.Duration&lt;/code&gt; like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; bcd &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &quot;@mdn/browser-compat-data&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; releaseDate&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Date&lt;/span&gt;&lt;span&gt;(bcd.__meta.timestamp);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`BCD release: v${&lt;/span&gt;&lt;span&gt;bcd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;__meta&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;}, ${&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// BCD release: v6.0.32, Tue Jul 15 2025 18:24:38 GMT+0200 (Central European Summer Time)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; temporalSupport&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; bcd.javascript.builtins.Temporal.Duration.__compat;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(temporalSupport);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//  mdn_url: &apos;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//  source_file: &apos;javascript/builtins/Temporal/Duration.json&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//  spec_url: &apos;https://tc39.es/proposal-temporal/#sec-temporal-duration-objects&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//  status: { deprecated: false, experimental: true, standard_track: true },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//  support: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//    chrome: { impl_url: &apos;https://crbug.com/42201538&apos;, version_added: false },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//    firefox: { version_added: &apos;139&apos; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//    safari: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//      impl_url: &apos;https://webkit.org/b/223166&apos;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//      version_added: &apos;preview&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//    },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//   ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s lots of reasons why knowing browser compat information is useful in projects, the most obvious one being static analysis of your code to give hints about feature support during development.&lt;/p&gt;
&lt;h2 id=&quot;detecting-missing-documentation&quot;&gt;Detecting missing documentation&lt;/h2&gt;
&lt;p&gt;One interesting thing is that each of the BCD entries has an &lt;code&gt;mdn_url&lt;/code&gt;, for example the &lt;code&gt;javascript.builtins.Temporal.Duration&lt;/code&gt; entry above has this member:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// mdn_url: &apos;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration&apos;,&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you look at that MDN page, you can see this mirrored in the &lt;a href=&quot;https://github.com/mdn/content/blob/b8f0c9066050467db45970f7d4433bdc5d67aaad/files/en-us/web/javascript/reference/global_objects/temporal/duration/index.md?plain=1#L1C1-L9C4&quot;&gt;&lt;code&gt;Temporal.Duration&lt;/code&gt; page front matter&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Temporal.Duration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Web/JavaScript/Reference/Global_Objects/Temporal/Duration&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;page-type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;javascript-class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;experimental&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;browser-compat&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;javascript.builtins.Temporal.Duration&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now we have a mapping between MDN, which contains the documentation, and BCD, which contains support data, identifiable by the BCD key &lt;code&gt;javascript.builtins.Temporal.Duration&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To build a small app with BCD data, I had a look through each of the entries and checked if it’s missing an &lt;code&gt;mdn_url&lt;/code&gt;.
This can happen if support information is added to BCD, but no documentation page for it has been written yet.
So let’s try to fetch some interesting BCD entries without an &lt;code&gt;mdn_url&lt;/code&gt;.
Basically, this is all we need to check CSS properties:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt; Object.&lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;(bcd.css.properties)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; compat&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; value.__compat;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // if there&apos;s support info, but no mdn_url, we&apos;re interested:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (compat.support &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;compat.mdn_url) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(key);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// -webkit-app-region&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// -webkit-border-horizontal-spacing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// -webkit-border-vertical-spacing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// …&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we’re more or less done!
The rest of the work is adding filtering and visualization logic.
It’s good to set a max depth to traverse for BCD data, because I’m generally interested in checking if &lt;code&gt;properties.align-self&lt;/code&gt; is missing &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/align-self&quot;&gt;an MDN URL&lt;/a&gt; but not &lt;code&gt;properties.align-self.stretch&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.thing&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  align-self&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;stretch&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn’t make that much sense to have dedicated pages for each property’s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/align-self#values&quot;&gt;value&lt;/a&gt; (like &lt;code&gt;stretch&lt;/code&gt;) as most of these keywords can’t be explained in isolation.&lt;/p&gt;
&lt;h2 id=&quot;check-missing-mdn-pages&quot;&gt;Check missing MDN pages&lt;/h2&gt;
&lt;p&gt;I deployed the example app here: &lt;a href=&quot;https://bcd.bsmth.de/&quot;&gt;https://bcd.bsmth.de/&lt;/a&gt; so you can have a look at the types of information you can explore.
It’s less important to have MDN pages for &lt;strong&gt;deprecated&lt;/strong&gt; and &lt;strong&gt;non-standard&lt;/strong&gt; things, so those are hidden by default.&lt;/p&gt;
&lt;p&gt;Bear in mind this is a &lt;strong&gt;basic example&lt;/strong&gt; app to get my creative juices flowing, and obviously there’s lots of technologies to check aside from CSS.
I haven’t decided if I want to have all technology categories on the same page or on separate pages (&lt;code&gt;/css&lt;/code&gt;, &lt;code&gt;/html&lt;/code&gt;), or to be able to switch between technology categories and focus on CSS only, JS only, HTTP only, via a dropdown.
Maybe there’s a better way to display the information other than tables, like cards?&lt;/p&gt;
&lt;p&gt;Anyway, I hope you find it interesting, and at the least you have some ideas about using BCD data in your projects.
If you want to fill in some of the documentation gaps, get in touch, or just &lt;a href=&quot;https://github.com/mdn/content&quot;&gt;go for it&lt;/a&gt; and don’t forget to mention me (&lt;code&gt;@bsmth&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdn/browser-compat-data&quot;&gt;&lt;code&gt;mdn/browser-compat-data&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://caniuse.com/&quot;&gt;caniuse&lt;/a&gt; - Website that shows browser support tables based on caniuse and BCD data.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/webstorm/&quot;&gt;JetBrains WebStorm&lt;/a&gt; - IDE that uses BCD data to &lt;a href=&quot;https://www.jetbrains.com/guide/javascript/tips/browser-compatibility-css/&quot;&gt;check browser support of used CSS properties&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mozilla.org/firefox/&quot;&gt;Mozilla Firefox&lt;/a&gt; - Web browser that uses BCD data in the DevTools to show &lt;a href=&quot;https://searchfox.org/mozilla-central/source/devtools/shared/compatibility/README.md&quot;&gt;CSS property compatibility data&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; - Programming language that uses BCD data to &lt;a href=&quot;https://github.com/microsoft/TypeScript-DOM-lib-generator&quot;&gt;generate DOM typings&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com&quot;&gt;Visual Studio Code&lt;/a&gt; - IDE that uses BCD to show compatibility information for &lt;a href=&quot;https://github.com/microsoft/vscode-custom-data/blob/c008a80baa3c6ea9d6757d2640eaab215b28f9a6/web-data/css/generateData.js#L349&quot;&gt;CSS features&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/web-features&quot;&gt;web-features&lt;/a&gt; - NPM package that publishes web feature groups with Baseline statuses based on BCD data.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>CSS</category><category>MDN</category></item><item><title>I&apos;m in the 512KB Club</title><link>https://bsmth.de/joining-the-512kb-club/</link><guid isPermaLink="true">https://bsmth.de/joining-the-512kb-club/</guid><description>My website&apos;s now listed on The 512KB Club, a large collection of small websites that show what&apos;s possible with an upper-limit of 512KB page weight.</description><pubDate>Fri, 09 May 2025 19:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Since February 2025, my website’s now listed on &lt;a href=&quot;https://512KB.club/&quot;&gt;The 512KB Club&lt;/a&gt;!
This collection of small websites is a showcase of what’s possible when you set out to do a lot, using only a little.
Let’s see what that’s all about and why it matters.&lt;/p&gt;
&lt;h2 id=&quot;performant-and-useful-websites&quot;&gt;Performant and useful websites&lt;/h2&gt;
&lt;p&gt;The 512KB Club is a curated list of websites that follow these two basic rules:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The website must be an actual site that contains a reasonable amount of information.&lt;/li&gt;
&lt;li&gt;The total &lt;strong&gt;uncompressed&lt;/strong&gt; web resources must not exceed 512KB.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order for a website to be included, you have to &lt;a href=&quot;https://radar.cloudflare.com/scan&quot;&gt;run a Cloudflare URL Scan&lt;/a&gt; on your website and if the uncompressed size is less than 512KB, you’re eligible.
Each site has to be submitted as a pull request with proof of scan results.
If it passes code review, you’re listed with all the other gloriously-small websites at &lt;a href=&quot;https://512KB.club/&quot;&gt;512KB.club&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It’s a neat idea, and it definitely nerd-sniped me because of the performance aspect.
I really like fast, slim websites, especially when they are bringing visitors in with the intention to read content.&lt;/p&gt;
&lt;h2 id=&quot;the-club-promotes-usability-as-a-side-effect&quot;&gt;The club promotes usability as a side-effect&lt;/h2&gt;
&lt;p&gt;The 512KB Club is interesting for a number of reasons.
The first rule “must be an actual site that contains a reasonable amount of information” is crucial, and I love that this is rule #1.
The &lt;a href=&quot;https://512KB.club/faq&quot;&gt;512KB Club FAQ&lt;/a&gt; has a highlighted section that answers why ultra-minimal link pages don’t qualify:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The whole point of the 512KB Club is to showcase what can be done with 512KB of space.
Anyone can come along and make a &amp;lt;10KB site containing 3 lines of CSS and a handful of links to other pages.&lt;/p&gt;
&lt;p&gt;That’s NOT a good showcase of what can be done with 512KB.&lt;/p&gt;
&lt;p&gt;What we’re looking for are sites with interesting design concepts that prove 512KB is a load of space that you can do so much with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So this is not about code-golfing a page down to an absurd level, or sending plain text to clients in HTTP responses (small, but ugly?).
The goal is to be deliberate with the resources you’re using during development and what you send down the wire.&lt;/p&gt;
&lt;p&gt;A lot of modern websites are slow and massive, and this is consistently &lt;a href=&quot;https://httparchive.org/reports/page-weight&quot;&gt;trending upwards since the dawn of time&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/2025/page-weight.png&quot; alt=&quot;The sum of transfer size kilobytes of all resources requested by the page since 2011&quot; width=&quot;1140&quot; height=&quot;686&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The 512KB Club bucks this trend to show what developers can do with a small footprint, leaving plenty of breathing room.&lt;/p&gt;
&lt;p&gt;Another side-effect of chasing this metric is that it nudges people towards designing accessible websites.
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility&quot;&gt;Accessibility&lt;/a&gt; shouldn’t be a side-quest, but browsers are most efficient when they’re parsing and rendering properly-structured HTML, CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;Libraries and other tools (not to mention &lt;a href=&quot;https://www.youtube.com/watch?v=ZsFIvULxkHI&quot;&gt;AI-generated&lt;/a&gt; &lt;a href=&quot;https://adrianroselli.com/2025/05/do-not-publish-your-designs-on-the-web-with-figma-sites.html&quot;&gt;slop&lt;/a&gt;) often produce garbled, generated content in a way that negatively impacts accessibility.
If everything is flat &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s all the way down, assistive technologies like screen readers can’t present sensible information for people to coherently navigate.&lt;/p&gt;
&lt;p&gt;The bottom line is that a stripped-down, standards-based approach to web dev may be less exciting to build with, but it’s what the most amount of clients can support, enabling more people to use and enjoy your projects.&lt;/p&gt;
&lt;h2 id=&quot;some-picks-from-the-512kb-club&quot;&gt;Some picks from the 512KB Club&lt;/h2&gt;
&lt;p&gt;It’s no surprise that a lot of these pages are personal blogs, as this type of website lends itself to being hacked on.
A personal blog is great not only for communication, but for testing out different technologies, coding, or design patterns.
Here’s a few others I chose from the club in no particular order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://cli.club/&quot;&gt;cli.club&lt;/a&gt; [25.5Kb]:&lt;/strong&gt; A collection of CLI software covering a wide range of categories from messaging, music, text editing and more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://mattstein.com/&quot;&gt;mattstein.com&lt;/a&gt; [206Kb]:&lt;/strong&gt; Matt’s a designer, developer, and former technical writer who has a great site that showcases a lot of projects and talents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://bestmotherfucking.website/&quot;&gt;bestmotherfucking.website&lt;/a&gt; [5.4Kb]:&lt;/strong&gt; I can’t really omit this one due to infamy.
One to avoid if you’re sensitive to profanity, but this made a splash around 2017 in reaction to bloated web trends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://silviamaggidesign.com/&quot;&gt;silviamaggidesign.com&lt;/a&gt; [311Kb]:&lt;/strong&gt; Designer and Accessibility Specialist Silvia Maggi has a website with a nice aesthetic that’s also easy to read and navigate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://lichess.org/&quot;&gt;lichess.org&lt;/a&gt; [495Kb]:&lt;/strong&gt; The awesome Lichess is a free/libre, open-source chess server powered by volunteers and donations.
It just scrapes into club membership with 17Kb to spare.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.slowernews.com/&quot;&gt;slowernews.com&lt;/a&gt; [169Kb]:&lt;/strong&gt; A curated site of notable news items, things likely to trend on Hacker News.
It looks like new content arrives &lt;a href=&quot;https://github.com/slowernews/slowernews/commits/master/&quot;&gt;a few times per month&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aside from these, I would recommend taking a look through some of the pages at random as the directory is a mix of different types of pages, and it’s a unique way to discover something new.
I think the best ones live somewhere in the middle in terms of page weight, as sites around 200Kb will be balancing performance and delivering engaging and nice-looking content.&lt;/p&gt;
&lt;aside&gt;
If you&apos;re interested in learning web development, I can highly recommend you build a blog or a personal website.
It&apos;s a low-risk way to grow practical experience, giving you something to show for your efforts, and you&apos;ll have something you own on the web that you control.
&lt;/aside&gt;
&lt;h2 id=&quot;keeping-it-performant&quot;&gt;Keeping it performant&lt;/h2&gt;
&lt;p&gt;My PR was merged on February 13th, so a big thanks to the maintainers for including me. The 512kb Club has since &lt;a href=&quot;https://kevquirk.com/sunsetting-the-512kb-club&quot;&gt;changed ownership to Brad Taunt&lt;/a&gt; and is now managed on SourceHut.
I’m delighted to be part of the club so that I can now show off this beautiful Orange Team banner on my site:&lt;/p&gt;
&lt;a href=&quot;https://512KB.club&quot;&gt;
  &lt;img class=&quot;banner-512&quot; src=&quot;https://bsmth.de/assets/images/orange-team.svg&quot; alt=&quot;a proud member of the orange team of 512KB club&quot; width=&quot;248&quot; height=&quot;30&quot; loading=&quot;lazy&quot;&gt;
&lt;/a&gt;
&lt;p&gt;Thanks a lot for reading! Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any thoughts or recommendations.&lt;/p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/from?site=512KB.club&quot;&gt;512KB Club on Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kevquirk.com/sunsetting-the-512kb-club&quot;&gt;Wanna Help With The 512KB Club?&lt;/a&gt; on kevquirk.com (2021)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://250kb.club/&quot;&gt;250kb.club&lt;/a&gt; like 512KB but… half the size.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://1mb.club/&quot;&gt;1mb.club&lt;/a&gt; like 512KB but… twice the size.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marginalia-search.com/&quot;&gt;Marginalia Search&lt;/a&gt; search engine for text-oriented websites indexing the small, old and weird web.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>CSS</category><category>HTML</category><category>JavaScript</category></item><item><title>2024: MDN HTTP content, GitHub, Firefox</title><link>https://bsmth.de/mdn-wrapped-2024/</link><guid isPermaLink="true">https://bsmth.de/mdn-wrapped-2024/</guid><description>My open source work in 2024 on GitHub, some stats, visualizations, and a summary of the types of work I focused on.</description><pubDate>Sat, 11 Jan 2025 10:13:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s that time of the year again - 2024 is &lt;del&gt;coming to a close&lt;/del&gt; wrapped.
Let’s look at what I did over the last year on GitHub.
I’ll share some stats that I collected from my work, visualize the data to understand it a little more, and show the categories and types of work I focused on.&lt;/p&gt;
&lt;h2 id=&quot;mdn-github-prs-in-2024&quot;&gt;MDN GitHub PRs in 2024&lt;/h2&gt;
&lt;p&gt;A lot of the work that goes into keeping MDN running happens in our GitHub organization, which has about &lt;a href=&quot;https://github.com/mdn&quot;&gt;40 active repos&lt;/a&gt;.
As is tradition, let’s have a look at the count of my PRs in these repos each month:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bsmth.de/assets/charts/stacked_bar_prs_by_repository.svg&quot; alt=&quot;Total pull requests by @bsmth in MDN Web Docs GitHub repositories by month.&quot; width=&quot;700&quot; height=&quot;500&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;p&gt;March 2024 was the busiest month in terms of PRs opened, with a total of 35 PRs landing (down from the 44 I made in August 2023).
May and October 2024 seemed like they were quiet enough, but as usual opening PRs is not everything we do.
There’s a lot of housekeeping of the GitHub organization, discussions, our other online community spaces, etc.&lt;/p&gt;
&lt;aside&gt;
Not all PRs are created equally, and it&apos;s silly to equate a PR to a unit of work, but let&apos;s be handsy-wavey for the sake of an overview.
&lt;/aside&gt;
&lt;details&gt;
&lt;summary&gt;Get this data&lt;/summary&gt;
&lt;p&gt;You can get this data (at least the public bits) yourself using the &lt;code&gt;gh&lt;/code&gt; CLI:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;
gh api --paginate \
  &quot;search/issues?q=author:bsmth+org:mdn+type:pr+created:2024-01-01..2024-12-31&quot; \
  --jq &apos;.items[] | [.title, .number, .state, .html_url, .created_at] | @csv&apos; \
  &gt; 2024_bsmth_prs.csv
&lt;/pre&gt;
&lt;/details&gt;
&lt;h3 id=&quot;prs-by-repository&quot;&gt;PRs by repository&lt;/h3&gt;
&lt;p&gt;I created &lt;strong&gt;274 PRs&lt;/strong&gt; across 23 public MDN GitHub repositores in 2024.
This is slightly lower than last the 281 PRs in 25 repositories from 2023, but it’s actually surprisingly close.
Usually, I’m working on &lt;strong&gt;mdn/content&lt;/strong&gt; and &lt;strong&gt;mdn/browser-compat-data&lt;/strong&gt; repos, but in 2024, I did a lot of editing on the &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/&quot;&gt;MDN Blog&lt;/a&gt;.
After the blog, the shared-assets repository was the number 3 repo that I contributed to, and I’ll go into more detail on that later.&lt;/p&gt;
&lt;h3 id=&quot;the-year-of-conventional-commits&quot;&gt;The year of conventional commits&lt;/h3&gt;
&lt;p&gt;I first started trying &lt;a href=&quot;https://www.conventionalcommits.org/en/v1.0.0/&quot;&gt;conventional commits&lt;/a&gt; to make my PRs a bit more structured &lt;a href=&quot;https://github.com/questdb/questdb/pull/778&quot;&gt;somewhere around 2021&lt;/a&gt;.
Over the years, I see more and more projects using this as a way to generate release notes and otherwise categorize changes, so I do this regularly now.
In 2024, I did a lot better at sticking to this pattern.
I’ve said it before, it’s hard to use conventional commits when you work on a documentation project - everything is &lt;code&gt;docs:&lt;/code&gt; on MDN, so I have used &lt;code&gt;relnote:&lt;/code&gt; for Firefox, and a scope like &lt;code&gt;docs(http):&lt;/code&gt; for technology categories.&lt;/p&gt;
&lt;p&gt;Here’s a breakdown of the types and scope of MDN PRs in 2024:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://bsmth.de/assets/charts/sankey_pr_titles_no_cc_leaf.svg&quot; alt=&quot;Total pull requests by @bsmth in MDN Web Docs GitHub repositories by month.&quot; width=&quot;700&quot; height=&quot;500&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;p&gt;It appears I’m a fan of &lt;code&gt;chore:&lt;/code&gt; commits, and that’s a bit of a catch-all for “maintenance”, but there’s a lot of it, so it deserves to be noted.
This chart also highlights that HTTP was the biggest technology category I worked on, which is a relief, because it was my goal for Q3 and Q4 in 2024, which I’ll go into in the next section.
PRs using &lt;code&gt;feat:&lt;/code&gt; are usually additions of pages or code, so there was a good amount of new content added for HTTP docs.&lt;/p&gt;
&lt;h2 id=&quot;2024-goals-and-themes&quot;&gt;2024 goals and themes&lt;/h2&gt;
&lt;p&gt;I had three main goals that my work was guided by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MDN &lt;strong&gt;HTTP&lt;/strong&gt; documentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance&lt;/strong&gt; &amp;amp; chores&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt; release notes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s take a look at these categories, what they are, why they’re important, and a few highlights from each.&lt;/p&gt;
&lt;h3 id=&quot;mdn-http-documentation-area&quot;&gt;MDN HTTP documentation area&lt;/h3&gt;
&lt;p&gt;I wrote a good summary of these changes &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/mdn-2024-content-projects/#http_content_updates&quot;&gt;for the MDN 2024 recap&lt;/a&gt;.
It’s a topic I love, because HTTP is what keeps the web humming, powering data exchange between people, servers, browsers, and machines.&lt;/p&gt;
&lt;p&gt;MDN’s HTTP section has over 300 pages, made of &lt;strong&gt;61 response status codes&lt;/strong&gt; (like &lt;code&gt;200 (OK)&lt;/code&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418&quot;&gt;&lt;code&gt;418 (I’m a teapot)&lt;/code&gt;&lt;/a&gt;) and &lt;strong&gt;148 HTTP headers&lt;/strong&gt; for sending metadata and instructions back and forth, including Content Security Policy and Permissions Policy.
Guides describe HTTP’s evolution from version 0.9 to HTTP/3, covering topics from basic usage to crucial mechanisms like caching.&lt;/p&gt;
&lt;p&gt;The main point of the HTTP work was to update and improve examples, remove obsolete content, and standardize formatting across the entire section.
I didn’t get to everything, and there’s some stragglers still on my list to fix (HTTP/1.1-specific concepts) but I’m very happy with the progress, and the positive feedback from our readers is up 10% (absolute) in this section since I started.&lt;/p&gt;
&lt;p&gt;In the MDN summary post, I highlighted the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing&quot;&gt;&lt;code&gt;Server-Timing&lt;/code&gt; HTTP header&lt;/a&gt; as interesting, you can use to surface server metrics to a client:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;http&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;HTTP&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;1.1&lt;/span&gt;&lt;span&gt; 200&lt;/span&gt;&lt;span&gt; OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Content-Type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; application/json; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Content-Length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; 123&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;pubDate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Fri, 03 Jan 2025 10:41:21 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Server-Timing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; custom-metric;dur=123.45;desc=&quot;My custom metric&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- response body ---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, the &lt;a href=&quot;https://www.bbc.co.uk/iplayer&quot;&gt;BBC iPlayer&lt;/a&gt; is using the &lt;code&gt;Server-Timing&lt;/code&gt; header to send performance metrics about the service, visible in the &lt;strong&gt;Network &amp;gt; Timings&lt;/strong&gt; panel in Firefox.
The main point of sending these metrics is to grab them via the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Server_timing&quot;&gt;Server Timing API&lt;/a&gt; so you can act on server-side metrics in the same way as all the other metrics processed with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Performance_API&quot;&gt;Performance API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most of the work was adding content (&lt;code&gt;feat&lt;/code&gt;), and tidying up and fixing docs (&lt;code&gt;chore&lt;/code&gt;), but there were some release notes for &lt;a href=&quot;https://github.com/mdn/content/pull/36149&quot;&gt;Fx 132 that were interesting to work on&lt;/a&gt;, too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reading:&lt;/strong&gt; For brushing up on HTTP topics, I enjoyed &lt;a href=&quot;https://www.manning.com/books/http2-in-action&quot;&gt;HTTP/2 in Action&lt;/a&gt; by Barry Pollard (&lt;a href=&quot;https://github.com/tunetheweb&quot;&gt;@tunetheweb&lt;/a&gt;).
If you’re familiar with HTTP/1.1 and want to dive into how the successive protocol(s) work, I can really recommend the book.
Sending a big thanks to Barry.&lt;/p&gt;
&lt;h4 id=&quot;mermaid-diagrams-and-shared-assets&quot;&gt;Mermaid diagrams and shared assets&lt;/h4&gt;
&lt;p&gt;I’ve &lt;a href=&quot;https://github.com/orgs/mdn/discussions/631&quot;&gt;championed SVG and mermaid syntax&lt;/a&gt; for sequence diagrams on MDN, mostly for maintenance reasons, as it makes it easier to store sources for images that we usually need to update later.
Binaries and images in documentation are something to be really careful about adding, because they very often cause heartache.
The worst offenders in docs are screenshots of code or screenshots of products or software UIs.&lt;/p&gt;
&lt;p&gt;There’s typically always a better alternative than screenshots.
For code, add actual code blocks to your prose so you can detect, lint, spellcheck, and update them easily, among other reasons.
For product screenshots, like guides on setting up hosting on a specific provider, these go out of date almost instantly - official documentation is usually better to &lt;strong&gt;link to instead&lt;/strong&gt;.
I try to stick to SVG, where possible, but there are always exceptions (like fancy interactive diagrams), so it’s also good to be flexible when needed.&lt;/p&gt;
&lt;p&gt;I pushed a lot for images to be stored in a &lt;a href=&quot;https://github.com/mdn/shared-assets/tree/main/images/diagrams/http&quot;&gt;shared-assets&lt;/a&gt; repository, which allows us to reuse images instead of duplicate them in the source, and these are easier to write, read, maintain, and they look nice, too.&lt;/p&gt;
&lt;h3 id=&quot;github-chores--maintenance&quot;&gt;GitHub chores &amp;amp; maintenance&lt;/h3&gt;
&lt;p&gt;As usual, there’s a ton of ad-hoc, unplanned work that keeps us extremely busy.
We try very hard to keep up with everything that’s happening in the MDN GitHub organization, which includes moderating issues, pull requests, and discussions, replying to questions, talking to our community, partners, readers and contributors.
I hope we’ve managed to keep MDN inviting for people to learn and contribute, as well as engaging to work on for new and long-time collaborators.
We all love maintaining MDN as much as people love reading it, so I hope that shows.&lt;/p&gt;
&lt;h3 id=&quot;firefox-release-notes&quot;&gt;Firefox release notes&lt;/h3&gt;
&lt;p&gt;The MDN team document each &lt;a href=&quot;https://github.com/mdn/mdn/issues?q=is%3Aissue+label%3A%22Firefox+release%22&quot;&gt;Firefox release&lt;/a&gt; with release notes specifically for developers.
You can find everything in the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases&quot;&gt;Firefox release notes for developers&lt;/a&gt; pages.
We also document &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Experimental_features&quot;&gt;Experimental features in Firefox&lt;/a&gt; for things that are only available behind a preference (though configuration).
This might be interesting if you’re looking to try out new features before they’ve landed in stable releases.
One of the notable changes was documenting that HTTP/2 Server Push is now deactivated by default in most major browsers.&lt;/p&gt;
&lt;h3 id=&quot;writing-about-the-web&quot;&gt;Writing about the web&lt;/h3&gt;
&lt;p&gt;Lastly, these are things I wrote about on the MDN blog:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/javascript-intl-segmenter-i18n/&quot;&gt;Locale-sensitive text segmentation in JavaScript with &lt;code&gt;Intl.Segmenter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/html-details-exclusive-accordions/&quot;&gt;Exclusive accordions using the HTML &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/javascript-set-methods/&quot;&gt;New JavaScript &lt;code&gt;Set&lt;/code&gt; methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/using-the-page-visibility-api/&quot;&gt;Using the Page Visibility API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/interop2023-mdn-doc-updates/&quot;&gt;Interop 2023: MDN updates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/mdn-blog-one-year-on/&quot;&gt;A year of publishing the MDN Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;getting-hands-on-with-rust&quot;&gt;Getting hands-on with Rust&lt;/h3&gt;
&lt;p&gt;In February 2024, I took a 3-day “Introduction to Rust” course run by Florian Gilcher of &lt;a href=&quot;https://ferrous-systems.com&quot;&gt;Ferrous Systems GmbH&lt;/a&gt;.
My prior experience was documenting a Rust client for a time series database, and refreshing the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_Wasm&quot;&gt;Compiling from Rust to WebAssembly&lt;/a&gt; tutorial on MDN.
In the course, we &lt;a href=&quot;https://github.com/ferrous-systems/rust-exercises&quot;&gt;built a TCP server&lt;/a&gt; which was challenging, but great fun.
I learned a lot, and I plan to put some of this to use in &lt;a href=&quot;https://github.com/mdn/rari&quot;&gt;MDN’s new build system, rari&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;We made it to the end of my 2024 wrap-up post.
I want to send a big thanks to Ruth John and the rest of the MDN team for the support and guidance all year.
I wouldn’t have landed all my HTTP work if it wasn’t for &lt;a href=&quot;https://developer.mozilla.org/en-US/about#our_team_hamish_willee&quot;&gt;Hamish&lt;/a&gt; - thanks for the patience!&lt;/p&gt;
&lt;p&gt;Thanks a lot for reading, and let me know what your thoughts are, if I missed something, or if you have any other suggestions.
Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>GitHub</category><category>MDN</category><category>Firefox</category><category>Mozilla</category></item><item><title>Intl.Segmenter for l10n in JavaScript</title><link>https://bsmth.de/mdn-intl-segmenter/</link><guid isPermaLink="true">https://bsmth.de/mdn-intl-segmenter/</guid><description>Learn how to use Intl.Segmenter for locale-sensitive text segmentation in JavaScript to simplify localization, count words or sentences in different languages, and more.</description><pubDate>Tue, 10 Sep 2024 09:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this year, JavaScript &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter&quot;&gt;&lt;code&gt;Intl.Segmenter&lt;/code&gt;&lt;/a&gt; gained support in all three major browser engines, meaning it’s achieved &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility&quot;&gt;Baseline status&lt;/a&gt; of “newly available”.
Now, your applications can natively retrieve meaningful information from strings in a variety of locales in the latest browsers.&lt;/p&gt;
&lt;p&gt;This is great news for developers that need ergonomic l10n for locale-aware apps or UIs instead of custom handling or third-party libraries.
Let’s explore what this opens up with some hands-on examples.&lt;/p&gt;
&lt;h2 id=&quot;what-is-text-segmentation-used-for&quot;&gt;What is text segmentation used for?&lt;/h2&gt;
&lt;p&gt;Text segmentation is a way to divide text into units like characters, words, and sentences.
Let’s say you have the following Japanese text and you’d like to perform a word count:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;吾輩は猫である。名前はたぬき。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re unfamiliar with Japanese, you might try built-in string methods in your first attempt.
For English strings, a rough way to count the words is to split by space characters:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; str&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &amp;quot;How many words. Are there?&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; words&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; str.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(words);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// [&amp;quot;How&amp;quot;,&amp;quot;many&amp;quot;,&amp;quot;words.&amp;quot;,&amp;quot;Are&amp;quot;,&amp;quot;there?&amp;quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(words.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// 5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The punctuation is mixed in with the word matches, and this will be inaccurate, but it’s a good approximation.
The problem is we don’t have any spaces separating the characters in the Japanese string.
Maybe your next idea would be to reach for &lt;code&gt;str.length&lt;/code&gt; to count the characters.
Using string length, you’d get &lt;code&gt;15&lt;/code&gt;, and if you remove the full stops (&lt;code&gt;。&lt;/code&gt;) you might guess &lt;strong&gt;13 words&lt;/strong&gt;.
The problem is we actually have &lt;strong&gt;8 words&lt;/strong&gt; in the string without punctuation:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#39;吾輩&amp;#39; &amp;#39;は&amp;#39; &amp;#39;猫&amp;#39; &amp;#39;で&amp;#39; &amp;#39;ある&amp;#39; &amp;#39;名前&amp;#39; &amp;#39;は&amp;#39; &amp;#39;たぬき&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you rely on string methods for a word count, you’ll quickly run into trouble as you can’t reliably split by specific character and you can’t use spaces as separators like you can in English.&lt;/p&gt;
&lt;p&gt;This is what locale-sensitive segmentation is built for.
The format for creating a segmenter in the &lt;code&gt;Intl&lt;/code&gt; namespace is as follows:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(locales, options);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s try passing the string into the segmenter with the &lt;code&gt;ja-JP&lt;/code&gt; locale for Japanese, and we explicitly set each segment to be of word-level granularity:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; jaSegmenter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;ja-JP&amp;quot;&lt;/span&gt;&lt;span&gt;, { granularity: &lt;/span&gt;&lt;span&gt;&amp;quot;word&amp;quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; segments&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; jaSegmenter.&lt;/span&gt;&lt;span&gt;segment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;吾輩は猫である。名前はたぬき。&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(segments));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example logs the following array to the console:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;segment&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;吾輩&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;index&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;input&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;吾輩は猫である。名前はたぬき。&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;isWordLike&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;segment&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;は&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;index&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;input&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;吾輩は猫である。名前はたぬき。&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;isWordLike&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;segment&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;猫&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;index&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;input&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;quot;吾輩は猫である。名前はたぬき。&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;quot;isWordLike&amp;quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // etc.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each item in the array, we get the segment, it’s index as it appears in the original string, the full input string, and a Boolean &lt;code&gt;isWordLike&lt;/code&gt; to disambiguate words from punctuation etc.
Now we have a robust and structured way to interact with the words that is locale-aware.
The segmenter’s granularity is &lt;code&gt;word&lt;/code&gt; in this example, so we can filter each item based on whether it’s &lt;code&gt;isWordLike&lt;/code&gt; to ignore punctuation:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; jaString&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &amp;quot;吾輩は猫である。名前はたぬき。&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; jaSegmenter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;ja-JP&amp;quot;&lt;/span&gt;&lt;span&gt;, { granularity: &lt;/span&gt;&lt;span&gt;&amp;quot;word&amp;quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; segments&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; jaSegmenter.&lt;/span&gt;&lt;span&gt;segment&lt;/span&gt;&lt;span&gt;(jaString);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; words&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(segments)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.isWordLike)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.segment);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(words);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// [&amp;quot;吾輩&amp;quot;,&amp;quot;は&amp;quot;,&amp;quot;猫&amp;quot;,&amp;quot;で&amp;quot;,&amp;quot;ある&amp;quot;,&amp;quot;名前&amp;quot;,&amp;quot;は&amp;quot;,&amp;quot;たぬき&amp;quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(words.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// 8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks much better.
We have an array with Japanese words using the segmenter, ready for adding locale-aware word count to our application.
We’ll explore that use case a bit more with a small example in the following sections.
Before that, we’ll take a look at the rest of the options that you can pass into a segmenter.&lt;/p&gt;
&lt;h2 id=&quot;intlsegmenter-options-and-configuration&quot;&gt;Intl.Segmenter options and configuration&lt;/h2&gt;
&lt;p&gt;We’ve seen above that you can split input by &lt;code&gt;word&lt;/code&gt; according to the locale.
If you don’t pass any options, the default behavior is to split by &lt;code&gt;grapheme&lt;/code&gt;, which is the user-perceived &lt;strong&gt;character&lt;/strong&gt;.
This is useful if you’re doing a character count on strings in different encodings or languages where characters are made of combined characters such as &lt;code&gt;किंतु&lt;/code&gt; in Hindi:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; str&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &amp;quot;किंतु&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(str.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// 5 &amp;lt;- oops&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hindiSegmenter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hindiSegments&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; hindiSegmenter.&lt;/span&gt;&lt;span&gt;segment&lt;/span&gt;&lt;span&gt;(str);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hiGraphemes&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(hindiSegments).&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.segment);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(hiGraphemes);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// [&amp;quot;किं&amp;quot;,&amp;quot;तु&amp;quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(hiGraphemes.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// 2 &amp;lt;- looks better&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last option you might need is to segment text by sentence, which is also very convenient if you don’t want to keep track of language-specific full-stops.
Some languages may use a period character &lt;code&gt;.&lt;/code&gt;, but this is not always consistent.
Let’s take the following example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hindiText&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &amp;quot;वाक्य एक। वाक्य दो।&amp;quot;&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// &amp;lt;- what do I split on here?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hiSegmenter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;&lt;span&gt;, { granularity: &lt;/span&gt;&lt;span&gt;&amp;quot;sentence&amp;quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hiSegments&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; hiSegmenter.&lt;/span&gt;&lt;span&gt;segment&lt;/span&gt;&lt;span&gt;(hindiText);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; hiSentences&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(hiSegments).&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.segment);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(hiSentences);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// [&amp;quot;वाक्य एक। &amp;quot;,&amp;quot;वाक्य दो।&amp;quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(hiSentences.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// 2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In another Hindi example, we have a character that looks similar to a pipe (&lt;code&gt;।&lt;/code&gt;) that’s separating sentences.
Now you don’t have to track Western periods or other locale-specific equivalents to split text into sentences.&lt;/p&gt;
&lt;p&gt;If you want to check support for a locale, you can use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/supportedLocalesOf&quot;&gt;&lt;code&gt;supportedLocalesOf&lt;/code&gt;&lt;/a&gt;.
This returns an array with the provided locales that are supported in segmentation without having to fall back to the default locale.
The following checks if the segmenter can use Hindi, Japanese, and German for segmentation:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(Intl.Segmenter.&lt;/span&gt;&lt;span&gt;supportedLocalesOf&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&amp;quot;hi&amp;quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;quot;ja-JP&amp;quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;quot;de&amp;quot;&lt;/span&gt;&lt;span&gt;]));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Array [&amp;quot;hi&amp;quot;, &amp;quot;ja-JP&amp;quot;, &amp;quot;de&amp;quot;] &amp;lt;- all are supported&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;japanese-locale-word-count-example&quot;&gt;Japanese locale word count example&lt;/h2&gt;
&lt;p&gt;If your browser supports &lt;code&gt;Intl.Segmenter&lt;/code&gt;, you can try out the following example.
There’s some Japanese text from Wikipedia, and a &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; element below it to show the output of our script.&lt;/p&gt;


&lt;p&gt;If you’ve followed all of the snippets so far, you shouldn’t see anything surprising here.
The only difference is that we’re getting the selected text using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection&quot;&gt;&lt;code&gt;window.getSelection&lt;/code&gt;&lt;/a&gt; before passing that into the segmenter which we’ve wrapped in a function.
After that, we’re listening for the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event&quot;&gt;&lt;code&gt;mouseup&lt;/code&gt;&lt;/a&gt; event when it’s fired on the paragraph and adding the output of the &lt;code&gt;countSelection&lt;/code&gt; function to the &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; countSelection&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; selection&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; window.&lt;/span&gt;&lt;span&gt;getSelection&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; selectedText&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; selection.&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; jaSegmenter&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Intl.&lt;/span&gt;&lt;span&gt;Segmenter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;ja-JP&amp;quot;&lt;/span&gt;&lt;span&gt;, { granularity: &lt;/span&gt;&lt;span&gt;&amp;quot;word&amp;quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; segments&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; jaSegmenter.&lt;/span&gt;&lt;span&gt;segment&lt;/span&gt;&lt;span&gt;(selectedText);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; words&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Array.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(segments)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.isWordLike)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; item.segment);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;word-count&amp;quot;&lt;/span&gt;&lt;span&gt;).textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    `Word count: ${&lt;/span&gt;&lt;span&gt;words&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt; - &amp;quot;${&lt;/span&gt;&lt;span&gt;words&lt;/span&gt;&lt;span&gt;}&amp;quot;`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;text-content&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  .&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;mouseup&amp;quot;&lt;/span&gt;&lt;span&gt;, countSelection);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To try it out, select some of the Japanese text below with your mouse.
On &lt;code&gt;mouseup&lt;/code&gt;, we log the word count along with the output of the segmenter with word-level granularity:&lt;/p&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;Locale-sensitive text segmentation with JavaScript is now more ergonomic in the latest browsers.
This feature is particularly useful for handling non-Latin languages, where the usual string manipulation methods will be unreliable.
If your app needs to handle multiple locales and you’re regularly working with text manipulation, &lt;code&gt;Intl.Segmenter&lt;/code&gt; can help segment text by word, characters, or sentences based on locale.
This simplifies tasks such as word or character count, sentence-splitting, string comparisons and more advanced text processing.&lt;/p&gt;
&lt;p&gt;Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any feedback or if you want to say hi.
See you next time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Honorable mentions:&lt;/strong&gt; Thanks to these people who gave me a shout-out for my blog post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tomaszs2.medium.com/javascript-intl-supports-word-and-sentence-segmentation-1a027c02efaa&quot;&gt;JavaScript Intl Supports Word and Sentence Segmentation&lt;/a&gt; by Tom Smykowski on medium.com (2024)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;p&gt;If you want to learn more about &lt;code&gt;Intl.Segmenter&lt;/code&gt;, you can have a read through these other resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter&quot;&gt;Intl.Segmenter&lt;/a&gt; MDN reference&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/blog/intl-segmenter&quot;&gt;The Intl.Segmenter object is now part of Baseline&lt;/a&gt; on web.dev (2024)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://polypane.app/blog/using-the-intl-segmenter-api/&quot;&gt;Using the Intl segmenter API&lt;/a&gt; polypane.app (2023)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://javascript.plainenglish.io/international-text-segmentation-with-intl-segmenter-in-javascript-639eb1865263&quot;&gt;International Text Segmentation with Intl.Segmenter in JavaScript&lt;/a&gt; by Zachary Lee (2023)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ayc0.github.io/posts/others/intlsegmenter-dont-use-stringsplit-nor-stringlength/&quot;&gt;Intl.Segmenter(): Don’t use string.split() nor string.length&lt;/a&gt; on ayc0.github.io (2023)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>JavaScript</category><category>MDN</category><category>Baseline</category></item><item><title>New exclusive accordions in HTML</title><link>https://bsmth.de/mdn-html-details-exclusive-accordions/</link><guid isPermaLink="true">https://bsmth.de/mdn-html-details-exclusive-accordions/</guid><description>The &apos;name&apos; attribute of the HTML details element is gaining more support across browsers. Learn how this feature allows creating exclusive accordions without scripting widgets from scratch.</description><pubDate>Tue, 13 Aug 2024 09:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Giving the same &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#name&quot;&gt;&lt;code&gt;name&lt;/code&gt;&lt;/a&gt; to a group of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;&lt;/a&gt; elements makes them behave like exclusive accordions.
This is good news for developers who have been writing custom accordions from scratch because it’s soon supported across all major browsers.
Let’s explore what this means with some quick examples.&lt;/p&gt;
&lt;p&gt;This feature’s stable in &lt;a href=&quot;https://chromestatus.com/feature/6710427028815872&quot;&gt;Chrome 120&lt;/a&gt;, &lt;a href=&quot;https://developer.apple.com/documentation/safari-release-notes/safari-17_2-release-notes#HTML&quot;&gt;Safari 17.2&lt;/a&gt; and is enabled in &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1856460&quot;&gt;Firefox Nightly&lt;/a&gt;, so you can already start trying it out.
While you’re testing this out, be sure to &lt;strong&gt;read the&lt;/strong&gt; &lt;a href=&quot;#accessibility-concerns&quot;&gt;&lt;strong&gt;accessibility concerns&lt;/strong&gt; section&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-does-details-work&quot;&gt;How does &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; work?&lt;/h2&gt;
&lt;p&gt;If you’re unfamiliar with &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;, it’s an element that creates a ‘disclosure widget’ in which information is visible only when the widget is toggled to an “open” state.
You should include in it a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary&quot;&gt;&lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;&lt;/a&gt; element, which is used for the text describing the disclosure.
If you omit the &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;, a browser-specific string like “Details” will be used by default.
Click the two elements below to toggle them between open and closed states and notice the missing &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; in the first one:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    The response phrase for 413 &amp;quot;Content Too Large&amp;quot; used to be &amp;quot;Payload Too&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Large&amp;quot;, and this message is still widely used.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;System configuration&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;200GB RAM&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;4PB storage&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;Since an accordion is a series of expandable sections that allow users to show or hide content, a series of &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements can be used to create an accordion-like interface.
Using the &lt;code&gt;name&lt;/code&gt; attribute, these elements can mimic “exclusive accordions”, meaning &lt;strong&gt;only one can be open&lt;/strong&gt; and other sections automatically close when a new one is opened.&lt;/p&gt;
&lt;p&gt;There’s great potential for &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements to fit into your pages because some minimal styling can go a long way to create interactive accordions using only HTML and CSS.&lt;/p&gt;

&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;System configuration&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;200GB RAM&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;4PB storage&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Recommended settings&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Extreme mode: on&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Raytracing: enabled&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Other details&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Material: Faux Leather, Metal&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Item Weight: 10.2Kg&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;using-details-name-to-create-exclusive-accordions&quot;&gt;Using &lt;code&gt;&amp;lt;details name&amp;gt;&lt;/code&gt; to create exclusive accordions&lt;/h2&gt;
&lt;p&gt;The addition of the &lt;code&gt;name&lt;/code&gt; attribute lets you connect multiple &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements and make an accordion exclusive, so only one &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element with the same name can be open at any time.
The browser toggles all others with the same name to “closed”.
It’s quite nice to be able to include this when you want to group multiple &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements:&lt;/p&gt;

&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;tech-specs&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;System configuration&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;200GB RAM&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;4PB storage&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;tech-specs&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Recommended settings&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Extreme mode: on&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Raytracing: enabled&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;tech-specs&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Other details&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Material: Faux Leather, Metal&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;Item Weight: 10.2Kg&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;An FAQ is one of the common uses, where the visitor can open one topic at a time:&lt;/p&gt;

&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;faq&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;Can I request a refund?&amp;lt;/&lt;/span&gt;&lt;span&gt;summary&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Yes, you have up to 30 days to request a refund. See our T&amp;amp;C for details.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;details&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;!-- etc. --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;accessibility-concerns&quot;&gt;Accessibility concerns&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.scottohara.me/blog/2022/09/12/details-summary.html&quot;&gt;Scott O’Hara&lt;/a&gt;, &lt;a href=&quot;https://yatil.net/blog/exclusive-accordions&quot;&gt;Eric Eggert&lt;/a&gt; and &lt;a href=&quot;https://adrianroselli.com/2023/08/progressively-enhanced-html-accordion.html&quot;&gt;Adrian Roselli&lt;/a&gt; have all written multiple times about accessibility issues with this pattern.
Notably, widgets &lt;a href=&quot;https://chromium-review.googlesource.com/c/chromium/src/+/5062340&quot;&gt;can’t tell you how many disclosures there are&lt;/a&gt; and your current position within a group, nor can you ‘expand all’ and ‘collapse all’ widgets.
Search is also not handled for collapsed widgets, so a &lt;kbd&gt;CMD&lt;/kbd&gt; + &lt;kbd&gt;F&lt;/kbd&gt; won’t match inside a collapsed widget. :weary:&lt;/p&gt;
&lt;p&gt;Others, like &lt;a href=&quot;https://web.archive.org/web/20250114102157/https://www.inclusion.hsbc.com/articles/common-mistakes/accordions-and-tab-panels&quot;&gt;HSBC Accessibility Hub&lt;/a&gt; and Florian Schroiff from A11Y Collective lists &lt;a href=&quot;https://www.a11y-collective.com/blog/accessible-accordion&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; as a good implementation example&lt;/a&gt; and says they “offer a semantic and accessible solution with less code” so there are mixed opinions whether promoting this pattern is a good thing.&lt;/p&gt;
&lt;p&gt;I lean towards Florian’s thoughts here, and that browsers should fill the gaps in poor and undefined behavior rather than hope good custom implementations propagate everywhere.
There are a lot of ways to create this pattern, and I think our best bet is native browser behavior, which will probably be used off-the-shelf in most cases and reach the most amount of people.&lt;/p&gt;
&lt;p&gt;These two examples were referenced in discussions if you want to have a look at accessible accordions that don’t use &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/accordion/examples/accordion/&quot;&gt;ARIA Authoring Practices Guide Accordion Example&lt;/a&gt; on w3.org&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://designsystem.digital.gov/components/accordion/&quot;&gt;U.S. Web Design System (USWDS) Accordion&lt;/a&gt; on designsystem.digital.gov&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;With more support landing for this feature in more stable browsers, it’s a good indicator that the web platform is providing better functionality for features that web developers have had to write themselves.
If you’re looking for a polyfill, there’s a great &lt;a href=&quot;https://developer.chrome.com/docs/css-ui/exclusive-accordion#polyfill_the_exclusive_accordion&quot;&gt;post by Bramus&lt;/a&gt;, which includes a polyfill for browsers that don’t support this yet.&lt;/p&gt;
&lt;p&gt;Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any feedback or if you want to say hi.
I hope you enjoyed this post and you’ll get more mileage out of the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element, which is a flexible and useful element for a lot of cases.
Don’t get too lost in the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and see you next time!&lt;/p&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;p&gt;If you want to learn more, you can have a read through these other resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#name&quot;&gt;MDN &lt;code&gt;&amp;lt;details name&amp;gt;&lt;/code&gt; reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://open-ui.org/components/accordion.explainer/&quot;&gt;Exclusive Accordion (Explainer)&lt;/a&gt; by &lt;a href=&quot;https://github.com/dbaron&quot;&gt;@dbaron&lt;/a&gt; (2023)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/css-ui/exclusive-accordion&quot;&gt;Exclusive Accordion&lt;/a&gt; by &lt;a href=&quot;https://www.bram.us/&quot;&gt;Bramus&lt;/a&gt; on developer.chrome.com (2023)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/blog/web-platform-12-2023&quot;&gt;New to the web platform in December&lt;/a&gt; on web.dev (2023)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>HTML</category><category>MDN</category><category>Baseline</category></item><item><title>New JavaScript Set methods</title><link>https://bsmth.de/mdn-javascript-set-methods/</link><guid isPermaLink="true">https://bsmth.de/mdn-javascript-set-methods/</guid><description>The JavaScript Set object has new methods that now have cross-browser support, so let&apos;s have a look at what they can do and why they&apos;re interesting.</description><pubDate>Wed, 26 Jun 2024 09:13:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s some fresh JavaScript Set features that have just achieved cross-browser support, so let’s have a look at what they can do.
Read on if you’re new to &lt;code&gt;Set&lt;/code&gt; in JavaScript and you’re curious how to use these new JavaScript methods.
I’ll highlight some advantages with basic examples to show why you might reach for these instead of building your own implementations.&lt;/p&gt;
&lt;h2 id=&quot;whats-new-in-set-methods&quot;&gt;What’s new in Set methods?&lt;/h2&gt;
&lt;p&gt;For those of you looking for the TL;DR, here’s the gist of new methods that have cross-browser support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection&quot;&gt;&lt;code&gt;intersection()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;new set&lt;/strong&gt; with the elements that are in &lt;strong&gt;both&lt;/strong&gt; this set and the given set.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union&quot;&gt;&lt;code&gt;union()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;new set&lt;/strong&gt; with all elements in this set and the given set.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/difference&quot;&gt;&lt;code&gt;difference()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;new set&lt;/strong&gt; with elements in this set but not in the given set.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference&quot;&gt;&lt;code&gt;symmetricDifference()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;new set&lt;/strong&gt; with elements in either set, but not in both.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSubsetOf&quot;&gt;&lt;code&gt;isSubsetOf()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;boolean&lt;/strong&gt; indicating if all elements of this set are in a another set.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSupersetOf&quot;&gt;&lt;code&gt;isSupersetOf()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;boolean&lt;/strong&gt; indicating if all elements of another set are in a this set.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isDisjointFrom&quot;&gt;&lt;code&gt;isDisjointFrom()&lt;/code&gt;&lt;/a&gt; returns a &lt;strong&gt;boolean&lt;/strong&gt; indicating if this set has no elements in common with another set.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re confused, don’t worry, we’ll describe what they do in the following sections.
In a nutshell, each of these methods are used to check what the contents of a set is compared to the contents of another set.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-javascript-set&quot;&gt;What is a JavaScript Set?&lt;/h2&gt;
&lt;p&gt;A set is similar to an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array&quot;&gt;Array&lt;/a&gt;, except that each value can only be stored once.
To demonstrate this, we can take a list of items, add them all to a set, and then inspect the results of the set.
Let’s see how these work in the following example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;My list&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;apple&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;pear&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;carrot&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;pear&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;apple&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;yoshi&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;My set&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;set&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s grab the &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; items, add them all to a new Set, and put them under &lt;code&gt;&amp;lt;ol id=&amp;quot;set&amp;quot;&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; allListItems&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; setList&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;set&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; mySet&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;allListItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  mySet.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(item.textContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// List the set contents in new &amp;lt;li&amp;gt; elements:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mySet.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; li&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  li.textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; item;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  setList.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(li);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the combined output, we have two ordered list elements.
The &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; on the right is the contents of the &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; list on the left, but it’s been converted to a set.
We have all duplicates removed from the list because we’re &lt;strong&gt;guaranteed that a set is unique&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;This might not look like much in a small example such as this, but it’s really convenient to have a built-in way to make unique collections of items, especially over larger data and more complex data types and objects.
For example, consider that you can add elements to a set like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; dogs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; yoshi&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; { name: &lt;/span&gt;&lt;span&gt;&amp;quot;Yoshi&amp;quot;&lt;/span&gt;&lt;span&gt;, personality: &lt;/span&gt;&lt;span&gt;&amp;quot;Friendly&amp;quot;&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dogs.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(yoshi);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s also typically faster to check if an element is in a set as opposed to in an Array, so this fits use cases where you have a large data structure and you need to keep an eye on performance of lookups.
You can also compose new sets with specific logical properties based on existing sets, and we’ll take a look at some examples of that below.&lt;/p&gt;
&lt;p&gt;We’ll be covering all of the new methods below, but if you want to see everything you can do with a &lt;code&gt;Set&lt;/code&gt;, check the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#instance_methods&quot;&gt;Instance methods&lt;/a&gt; documentation.&lt;/p&gt;
&lt;h2 id=&quot;union-of-two-sets&quot;&gt;Union of two sets&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;union&lt;/strong&gt; is a good place to start off if you’re not familiar with sets.
With the union, we can check what elements are in ‘either or both’ sets.
In the example below, we have two lists and we want to make a third one that contains all items from both lists, but without duplicates:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;Set one&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;set-one&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;apple&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;banana&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;pear&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;carrot&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;Set two&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;set-two&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;banana&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;peach&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;plum&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;apple&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;Union set&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;union&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; listOneItems&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;#set-one li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; listTwoItems&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;#set-two li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; allListItems&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; unionList&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;union&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Create sets for each list&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; set1&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; set2&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Add list items to each set&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;listOneItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  set1.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(item.textContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;listTwoItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  set2.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(item.textContent);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Create a union of both sets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; unionSet&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; set1.&lt;/span&gt;&lt;span&gt;union&lt;/span&gt;&lt;span&gt;(set2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// List the items in the union&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;unionSet.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; li&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  li.textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; item;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  unionList.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(li);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;We’re using the HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent&quot;&gt;&lt;code&gt;textContent&lt;/code&gt;&lt;/a&gt; of each list item so we have sets of strings, but you can see how this can be powerful if we consider that sets can contain data types like arrays or objects.&lt;/p&gt;
&lt;p&gt;This is helpful because we don’t need any custom implementations to remove duplicates, do equality checks, or make other comparisons.
Once we have elements in a set, we know they’re all unique, and the &lt;code&gt;union&lt;/code&gt; method is an optimized way to make a third set of (unique) items that appear in ‘either or both’ sets.&lt;/p&gt;
&lt;h2 id=&quot;set-intersection&quot;&gt;Set intersection&lt;/h2&gt;
&lt;p&gt;With set intersection, we can check what elements occur in both sets to see what the overlap is.
In this example, instead of displaying the intersection set as a third list like we did with &lt;code&gt;union&lt;/code&gt; above, we can use it to highlight elements that are ‘both sets only’:&lt;/p&gt;



&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Make the intersection set&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; intersectionSet&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; set1.&lt;/span&gt;&lt;span&gt;intersection&lt;/span&gt;&lt;span&gt;(set2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Loop through lists and highlight if they&amp;#39;re in the intersection&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;allListItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (intersectionSet.&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(item.textContent)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    item.className &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;quot;match&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;set-symmetric-difference&quot;&gt;Set symmetric difference&lt;/h2&gt;
&lt;p&gt;Before we look at difference, let’s look at &lt;code&gt;symmetricDifference&lt;/code&gt; which might sound complex, but hopefully the example below demystifies that.
What the symmetric difference does is lets us check which elements are in either set, but &lt;strong&gt;not both&lt;/strong&gt;.&lt;/p&gt;



&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; setNotBoth&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; set1.&lt;/span&gt;&lt;span&gt;symmetricDifference&lt;/span&gt;&lt;span&gt;(set2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;allListItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (setNotBoth.&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(item.textContent)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    item.className &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;quot;match&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;If this is all new and you’re struggling to understand, compare the &lt;em&gt;symmetric difference&lt;/em&gt; with the &lt;em&gt;intersection&lt;/em&gt; example.
You should see that the &lt;code&gt;symmetricDifference&lt;/code&gt; method does the opposite logical operation of the &lt;code&gt;intersection&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;set-difference&quot;&gt;Set difference&lt;/h2&gt;
&lt;p&gt;With set difference, we can check which elements are in one set but not in another.
For this example, we are creating two new sets instead of one.
The first one (&lt;code&gt;set1only&lt;/code&gt;) is created with &lt;code&gt;set1.difference(set2)&lt;/code&gt; and what we get back is a new set of items that are ‘set one only’.
We do the same with the new set &lt;code&gt;set2only&lt;/code&gt; to find items that are in set two but not in the first one.
For each list, we can use the sets created with &lt;code&gt;difference&lt;/code&gt; to highlight list items that don’t appear in the other list.&lt;/p&gt;



&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; set1only&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; set1.&lt;/span&gt;&lt;span&gt;difference&lt;/span&gt;&lt;span&gt;(set2);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; set2only&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; set2.&lt;/span&gt;&lt;span&gt;difference&lt;/span&gt;&lt;span&gt;(set1);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;allListItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (set1only.&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(item.textContent)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    item.className &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;quot;setOneMatch&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (set2only.&lt;/span&gt;&lt;span&gt;has&lt;/span&gt;&lt;span&gt;(item.textContent)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    item.className &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;quot;setTwoMatch&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;subset-superset-and-disjoint-from&quot;&gt;Subset, superset, and disjoint from&lt;/h2&gt;
&lt;p&gt;The last of the new methods and concepts that we can explore are subset, superset, and ‘disjoint from’ methods.
By now, you’re familiar with the set operations that return new sets.
The subset, superset, and disjoint from methods don’t return new sets, but a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean&quot;&gt;Boolean&lt;/a&gt; value to indicate certain state or logical checks.&lt;/p&gt;
&lt;p&gt;The first two we can look at are &lt;strong&gt;subset&lt;/strong&gt; and &lt;strong&gt;superset&lt;/strong&gt;.
Instead of highlighting list items, we have some statements or assertions like “Set one is a subset of set two”.
We then add some text (&lt;code&gt;TRUE&lt;/code&gt; / &lt;code&gt;FALSE&lt;/code&gt;) to the list item depending on the result of a check like &lt;code&gt;if (set1.isSupersetOf(set2))&lt;/code&gt;:&lt;/p&gt;


&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (set1.&lt;/span&gt;&lt;span&gt;isSubsetOf&lt;/span&gt;&lt;span&gt;(set2)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  oneSubTwo.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [TRUE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  oneSubTwo.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [FALSE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;So with &lt;strong&gt;subset&lt;/strong&gt;, we can check if all the items from a set appear in another set or not.
With &lt;strong&gt;superset&lt;/strong&gt;, we’re doing the opposite, to see if a set has all items from another set, with some additional items.
&lt;strong&gt;Note&lt;/strong&gt; that if the two sets are equal, they’ll satisfy both &lt;code&gt;isSubsetOf()&lt;/code&gt; and &lt;code&gt;isSupersetOf()&lt;/code&gt; methods:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; one&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; two&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Set&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(one.&lt;/span&gt;&lt;span&gt;isSubsetOf&lt;/span&gt;&lt;span&gt;(two)); &lt;/span&gt;&lt;span&gt;// true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(one.&lt;/span&gt;&lt;span&gt;isSupersetOf&lt;/span&gt;&lt;span&gt;(two)); &lt;/span&gt;&lt;span&gt;// true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last method for us to look at is &lt;code&gt;isDisjointFrom()&lt;/code&gt; where we can find out if two sets have &lt;strong&gt;no common elements&lt;/strong&gt;.
The first and second sets below are not disjoint from each other as they have one element (“C”) in common.
The third set is disjoint from the other two as it has no items in common with either set:&lt;/p&gt;



&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Assertions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; oneDisjointTwo&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;1disjointFrom2&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; twoDisjointThree&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;2disjointFrom3&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; threeDisjointOne&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;3disjointFrom1&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (set1.&lt;/span&gt;&lt;span&gt;isDisjointFrom&lt;/span&gt;&lt;span&gt;(set2)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  oneDisjointTwo.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [TRUE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  oneDisjointTwo.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [FALSE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (set2.&lt;/span&gt;&lt;span&gt;isDisjointFrom&lt;/span&gt;&lt;span&gt;(set3)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  twoDisjointThree.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [TRUE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  twoDisjointThree.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [FALSE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (set3.&lt;/span&gt;&lt;span&gt;isDisjointFrom&lt;/span&gt;&lt;span&gt;(set1)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  threeDisjointOne.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [TRUE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  threeDisjointOne.textContent &lt;/span&gt;&lt;span&gt;+=&lt;/span&gt;&lt;span&gt; &amp;quot; [FALSE]&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;I hope you enjoyed this post about &lt;code&gt;Set&lt;/code&gt; methods and why I think sets are an interesting concept to understand.
Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any feedback or if you want to say hi.
You should be all &lt;code&gt;Set&lt;/code&gt; for your next project, so see you next time!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Honorable mentions:&lt;/strong&gt; Thanks to these people who gave me a shout-out for my blog post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/blog/new-javascript-set-methods/&quot;&gt;New JavaScript Set methods&lt;/a&gt; by Chris Coyier on frontendmasters.com&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;see-also&quot;&gt;See also&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/blog/set-methods&quot;&gt;The JavaScript Set methods are now part of Baseline&lt;/a&gt; by Adriana Jara on web.dev&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=40772387&quot;&gt;Read the Hacker News discussion&lt;/a&gt; about this post&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>GitHub</category><category>JavaScript</category><category>MDN</category><category>Baseline</category></item><item><title>Page Visibility API usage &amp; hints</title><link>https://bsmth.de/mdn-page-viz/</link><guid isPermaLink="true">https://bsmth.de/mdn-page-viz/</guid><description>This post takes a look at what page visibility is, how you can use the Page Visibility API in your websites, and describes pitfalls to avoid if you build features around this functionality.</description><pubDate>Fri, 10 May 2024 10:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Checking document visibility gives you insight into how visitors are interacting with your pages and can provide hints about the status of your applications.
This post takes a look at what page visibility is, how you can use the Page Visibility API in your websites, and describes pitfalls to avoid if you build features around this functionality.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API&quot;&gt;Page Visibility API&lt;/a&gt; lets you find out the visibility of a page and set up event listeners to do something when page visibility changes.
Let’s look at what page visibility means, how you can use this API, and some common pitfalls to avoid.&lt;/p&gt;
&lt;h2 id=&quot;whats-the-page-visibility-api&quot;&gt;What’s the Page Visibility API?&lt;/h2&gt;
&lt;p&gt;The Page Visibility API was developed as a &lt;a href=&quot;https://www.w3.org/TR/page-visibility-2/&quot;&gt;standalone specification&lt;/a&gt;, but it’s been incorporated directly into the HTML spec under &lt;a href=&quot;https://html.spec.whatwg.org/multipage/interaction.html#page-visibility&quot;&gt;Page visibility&lt;/a&gt;.
This API exposes a way to determine a document’s visibility state so you can check if a web page is “visible” or “hidden”.
Sounds straightforward, but pages can be “hidden” in several different ways.&lt;/p&gt;
&lt;p&gt;A page is “hidden” if the browser window with the page is minimized, if it’s completely obscured by another application, or the user switches to another tab.
Page visibility also changes to “hidden” if the operating system’s screen lock is activated, so mobile device behavior is also taken into consideration.
By contrast, a page remains “visible” even when it’s partially visible on the screen.&lt;/p&gt;
&lt;h2 id=&quot;how-to-check-page-visibility-changes&quot;&gt;How to check page visibility changes&lt;/h2&gt;
&lt;p&gt;You can check the visibility of a document using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState&quot;&gt;&lt;code&gt;document.visibilityState&lt;/code&gt;&lt;/a&gt;, which will return either &lt;code&gt;visible&lt;/code&gt; or &lt;code&gt;hidden&lt;/code&gt;.
Alternatively, you can check the value of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/hidden&quot;&gt;&lt;code&gt;document.hidden&lt;/code&gt;&lt;/a&gt; Boolean property:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(document.visibilityState); &lt;/span&gt;&lt;span&gt;// &quot;visible&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(document.hidden); &lt;/span&gt;&lt;span&gt;// false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice, it’s convenient to use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilitychange_event&quot;&gt;&lt;code&gt;visibilitychange&lt;/code&gt;&lt;/a&gt; event so you can trigger logic when the visibility state of a page changes instead of checking the visibility manually:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // doSomething();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the state changes, you can do something depending on the &lt;code&gt;document.visibilityState&lt;/code&gt; or &lt;code&gt;document.hidden&lt;/code&gt; Boolean:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.hidden) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Page visibility is now &apos;hidden&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Page visibility is now &apos;visible&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s worth noting that there’s no &lt;code&gt;document.visible&lt;/code&gt;, so if you’re only interested in that state, you can use &lt;code&gt;document.visibilityState === &quot;visible&quot;&lt;/code&gt; (or &lt;code&gt;!document.hidden&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.visibilityState &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &quot;visible&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // The page is visible again!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;why-page-visibility-is-useful&quot;&gt;Why page visibility is useful&lt;/h2&gt;
&lt;p&gt;Page visibility is useful in many cases, but the most likely fits are managing usage of compute resources, in analytics, and adding functionality that can improve user experience (UX) on a range of devices.
Let’s take a look at these in more detail.&lt;/p&gt;
&lt;h3 id=&quot;session-lifecycle-in-analytics&quot;&gt;Session lifecycle in analytics&lt;/h3&gt;
&lt;p&gt;In the analytics domain, it’s common to record the point when pages change from &lt;code&gt;visible&lt;/code&gt; to &lt;code&gt;hidden&lt;/code&gt;.
A page changing to a &lt;code&gt;hidden&lt;/code&gt; state might be the last event that’s observable by a page, so developers often treat it as the end of the user’s session.
However, this heavily depends on how you define ‘a session’.
You may define a session based on a fixed period of inactivity as opposed to relying solely on ‘first page hide’.
Therefore, this use case will vary according to your needs:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.visibilityState &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &quot;hidden&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    navigator.&lt;/span&gt;&lt;span&gt;sendBeacon&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/log&quot;&lt;/span&gt;&lt;span&gt;, analyticsData);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;managing-resources-efficiently&quot;&gt;Managing resources efficiently&lt;/h3&gt;
&lt;p&gt;The use case I’m personally most excited about is the opportunity to &lt;strong&gt;stop doing something resource-intensive&lt;/strong&gt; when someone is &lt;strong&gt;no longer viewing the page&lt;/strong&gt;.
This capability is really powerful considering that it allows us to manage client (or even server) compute or bandwidth in a deliberate way.&lt;/p&gt;
&lt;p&gt;When it comes to managing resources, browsers already have some related features like &lt;a href=&quot;https://hacks.mozilla.org/2021/10/tab-unloading-in-firefox-93/&quot;&gt;tab unloading&lt;/a&gt; when system memory is critically low, and optimizing &lt;a href=&quot;https://archive.fosdem.org/2023/schedule/event/mozilla_firefox_energy_use&quot;&gt;what background tabs are doing&lt;/a&gt;.
We can still make our client-side applications more efficient, especially in areas that browsers don’t or can’t automatically handle.&lt;/p&gt;
&lt;p&gt;For example, when a page is hidden, your app can use lower bitrate video, or throttle real-time communication (WebSockets and WebRTC).
IndexedDB processes can be optimized as browsers don’t throttle these connections to avoid timeouts.
You can stop invisible animations or other visual media, timers, throttle network requests, stop polling endpoints, there are a lot of opportunities for efficiency gains.
You can find more information about what browsers do in the “Policies in place to aid background page performance” section on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API#policies_in_place_to_aid_background_page_performance&quot;&gt;Page Visibility API page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;improving-user-experience&quot;&gt;Improving user experience&lt;/h3&gt;
&lt;p&gt;When the page changes from &lt;code&gt;hidden&lt;/code&gt; to &lt;code&gt;visible&lt;/code&gt;, we could assume that a visitor has returned to our page, so we can restart anything that we may have paused when the page was hidden.
However, there are a few logical traps, especially concerning media, that you could fall into.
Therefore, you need to be careful when resuming actions in such cases.
We’ll look at examples of this in detail in the next section.&lt;/p&gt;
&lt;h2 id=&quot;patterns-to-avoid-with-page-visibility-checks&quot;&gt;Patterns to avoid with page visibility checks&lt;/h2&gt;
&lt;p&gt;User freedom is essential for your pages, so this API &lt;strong&gt;should be used with care&lt;/strong&gt; to ensure your visitors have agency while browsing.
In other words, give people control over when to start, resume, &lt;strong&gt;and skip&lt;/strong&gt; media on your pages.
You also shouldn’t assume that visitors want media to be paused automatically when a page is hidden.
Expose a preference or allow visitors to opt-in to this kind of functionality instead.&lt;/p&gt;
&lt;p&gt;There used to be a code snippet on MDN on the &lt;code&gt;visibilitychange&lt;/code&gt; page that was a good example of how this API can cause usability issues.
Can you spot what the problem is in the JavaScript code below?&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;audio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  controls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;audio&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; audio&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;audio&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.hidden) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    audio.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    audio.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is an &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element with controls visible so the user can start and stop the media.
In JavaScript, we’re looking at the &lt;code&gt;visibilitychange&lt;/code&gt; event and doing the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the page is hidden, pause the audio.&lt;/li&gt;
&lt;li&gt;If the page is shown, play the audio.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This sounds reasonable, but there’s actually a big problem.
If I navigate to the page, then switch to another tab and back again (or restore a browser window after it’s been minimized) audio will autoplay.
The point is that page visibility may toggle between &lt;code&gt;hidden&lt;/code&gt; and &lt;code&gt;visible&lt;/code&gt; without the user ever interacting with the &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;This situation becomes even more annoying if the &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element is located below the fold and is not visible on page load.
This is extremely frustrating if visitors need to hunt down where audio is playing after they’ve located which tab it’s coming from.&lt;/p&gt;
&lt;p&gt;To avoid usability issues, check if someone has interacted with media before resuming playback.
There may be better ways to approach this, but the fix I made to the MDN reference page example was to store the state of the audio player when the page is hidden.
When the page visibility changes to &lt;code&gt;visible&lt;/code&gt;, resume media playback &lt;em&gt;only if it was playing when the page was hidden&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let’s modify the event listener to store the audio state on page hide.
A boolean &lt;code&gt;playingOnHide&lt;/code&gt; is sufficient for this purpose; set it when the document’s visibility changes to &lt;code&gt;hidden&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Initialize as false; we&apos;re not auto playing audio when the page loads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.hidden) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Page changed to &quot;hidden&quot; state, store if audio playing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;audio.paused;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Pause audio if page is hidden&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    audio.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with &lt;code&gt;document.hidden&lt;/code&gt;, there is no &lt;code&gt;audio.playing&lt;/code&gt; state, so we have to check &lt;code&gt;!audio.paused&lt;/code&gt; (not paused) instead.
That means the &lt;code&gt;playingOnHide&lt;/code&gt; Boolean can be set to whatever the value of &lt;code&gt;!audio.paused&lt;/code&gt; is on page hide, and we’re pretty much done.
The only other state the visibility of the page can be is &lt;code&gt;visible&lt;/code&gt;, so in the &lt;code&gt;else&lt;/code&gt; statement, we handle the playing logic:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.hidden) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;audio.paused;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    audio.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Page became visible! Resume playing if audio was &quot;playing on hide&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (playingOnHide) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      audio.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The finished code looks like this with a tidy little gate for checking the audio state on page hide:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;audio&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  controls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;audio&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; audio&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;audio&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;document.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visibilitychange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (document.hidden) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    playingOnHide &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; !&lt;/span&gt;&lt;span&gt;audio.paused;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    audio.&lt;/span&gt;&lt;span&gt;pause&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (playingOnHide) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      audio.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;I hope you enjoyed this post about page visibility and why I think it opens up interesting opportunities for resource management, analytics, UX, and other use cases.
I’m looking forward to hearing your thoughts on different ways to use this API and what other efficiency gains developers can make use of that browsers don’t handle well.&lt;/p&gt;
&lt;p&gt;Let me know what your thoughts are, if I missed something, or if you have any other suggestions.
Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any feedback or if you want to say hi.&lt;/p&gt;
</content:encoded><category>JavaScript</category><category>HTML</category><category>MDN</category><category>Baseline</category></item><item><title>2023: GitHub, Interop, Firefox release notes.</title><link>https://bsmth.de/mdn-github-wrapped-2023/</link><guid isPermaLink="true">https://bsmth.de/mdn-github-wrapped-2023/</guid><description>My open source work in 2023 on GitHub, some stats, visualizations, and a summary of the types of work I focused on.</description><pubDate>Sun, 21 Jan 2024 10:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Now that 2023 is &lt;del&gt;coming to a close&lt;/del&gt; wrapped, I thought I’d take a look back at what I managed to achieve over the year.
I joined Mozilla in 2022 as a Staff Technical Writer on the MDN team, which made 2023 the first full year that I focused entirely on MDN Web Docs.
In this post, I’ll share some stats that I collected from my work on GitHub, visualize the data to understand it a little more, and show the categories I focused on.&lt;/p&gt;
&lt;h2 id=&quot;mdn-github-prs-in-2023&quot;&gt;MDN GitHub PRs in 2023&lt;/h2&gt;
&lt;p&gt;A lot of the work that goes into keeping MDN running happens in our GitHub organization which has about &lt;a href=&quot;https://github.com/mdn&quot;&gt;40 active repos&lt;/a&gt;.
Let’s have a look at the count of my PRs in these repos each month:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/2024/github-stats1.png&quot; alt=&quot;Total pull requests by @bsmth in MDN Web Docs GitHub repositories by month.&quot; width=&quot;989&quot; height=&quot;497&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;It looks like August was the busiest month, with 44 PRs landing.
It’s also pretty noticeable when I took parental leave as my productivity is just about halved over March-April and September-October.&lt;/p&gt;
&lt;h3 id=&quot;prs-by-repository&quot;&gt;PRs by repository&lt;/h3&gt;
&lt;p&gt;I created &lt;strong&gt;281 PRs&lt;/strong&gt; across 25 public MDN GitHub repositores last year.
The &lt;strong&gt;mdn/content&lt;/strong&gt; and &lt;strong&gt;mdn/browser-compat-data&lt;/strong&gt; repos are noteworthy, so we can split the chart above and highlight these against other public MDN repos as &lt;code&gt;mdn/other&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/2024/github-stats2.png&quot; alt=&quot;Pull requests by @bsmth by repository in MDN Web Docs GitHub organization by month.&quot; width=&quot;989&quot; height=&quot;497&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Although the content and BCD repositories are usually the primary sources to add and update data, we can see from this graph that a lot of maintenance (about 30% of my work) happens in other repos, also.
There is also &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/&quot;&gt;a Blog on MDN&lt;/a&gt; since May 2023 that I worked on, although this repo is private for now, so those contributions are not included in these stats.&lt;/p&gt;
&lt;h3 id=&quot;trying-to-stick-to-conventions&quot;&gt;Trying to stick to conventions&lt;/h3&gt;
&lt;p&gt;I tried to stick to &lt;a href=&quot;https://www.conventionalcommits.org/en/v1.0.0/&quot;&gt;conventional commits&lt;/a&gt; whenever I could, and my diligence improved over the course of the year.
In general, it’s a little tricky using the conventional commits per spec seeing as everything is arguably &lt;code&gt;docs:&lt;/code&gt; on MDN, so I’ve expanded on this scheme to use &lt;code&gt;relnote:&lt;/code&gt; for Firefox, and subcategories like &lt;code&gt;docs(HTTP):&lt;/code&gt; for site sections.
It’s not perfect, but it helps to get a rough overview of the types of work landing based on commit data:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/2024/github-stats3.png&quot; alt=&quot;Pull request titles following conventional commits by @bsmth in MDN Web Docs GitHub organization by month.&quot; width=&quot;989&quot; height=&quot;497&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;2023-goals-and-themes&quot;&gt;2023 goals and themes&lt;/h2&gt;
&lt;p&gt;I had three main goals that my work was guided by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Interop 2023&lt;/li&gt;
&lt;li&gt;Firefox release notes&lt;/li&gt;
&lt;li&gt;General maintenance &amp;amp; GitHub chores&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s take a look at these categories, what they are, why they’re important, and a few highlights from each.&lt;/p&gt;
&lt;h3 id=&quot;interop-23&quot;&gt;Interop ’23&lt;/h3&gt;
&lt;p&gt;Our team included &lt;a href=&quot;https://github.com/web-platform-tests/interop/tree/main/2023&quot;&gt;Interop ’23&lt;/a&gt; as part of our high-level documentation goals for the year.
The intention is that the focus areas in Interop 2023 have the corresponding MDN docs audited for freshness as the features and their support are evolving.
If you want to see other Interop 2023 tasks and PRs, check &lt;a href=&quot;https://github.com/mdn/mdn/issues/416&quot;&gt;this tracking issue on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Interop 2023 is a cross-browser effort to improve the interoperability of the web — to reach a state where each technology works exactly the same in every browser.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A lot of features &lt;a href=&quot;https://wpt.fyi/interop-2023&quot;&gt;reached stable status&lt;/a&gt; across all major browser engines in 2023 and that meant we could remove a lot of experimental warnings and update code to use the latest syntax.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/interop-2023.png&quot; alt=&quot;Interop 2023 dashboard as of January 2024&quot; width=&quot;1602&quot; height=&quot;1202&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;By chance, I happened to work quite a lot on CSS colors and this turned out to be a really interesting deep dive for me.
I’ve written a fair bit about CSS colors in 2023 here and on the MDN blog; you can find out more in my &lt;a href=&quot;https://bsmth.de/blog/&quot;&gt;posts about CSS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s some pull requests with some of my highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Diving into CSS custom properties:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/30451&quot;&gt;Adding more details to the “Using CSS Custom Properties” page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Making display more versatile:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/30831&quot;&gt;Expand on usage of multi-keyword &lt;code&gt;display&lt;/code&gt; values&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;firefox-release-notes&quot;&gt;Firefox release notes&lt;/h3&gt;
&lt;p&gt;A yearly goal for the MDN content team was making sure that the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases&quot;&gt;Firefox release notes for developers&lt;/a&gt; were updated in time for each stable release.
The ‘definition of done’ for each task is that there is a release note for changes affecting web developers, browser compatibility data is updated, and that corresponding reference and learning resources are audited and updated when necessary.&lt;/p&gt;
&lt;p&gt;Similarly to Interop, closely following the Firefox release process helps us keep docs fresh and gives the team insight on how the web platform is changing month over month.
Features are added or removed in Firefox based on relevance and developer feedback along with maintenance costs and other rationale, so keeping tabs on these changes helps us understand which features are noteworthy and why.&lt;/p&gt;
&lt;p&gt;Here’s a filtered list of PRs by month with &lt;code&gt;Fx&lt;/code&gt; or &lt;code&gt;relnote&lt;/code&gt; in the title:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/2024/github-stats4.png&quot; alt=&quot;Pull requests by month with Fx or relnote in the title by @bsmth in MDN Web Docs GitHub organization by month.&quot; width=&quot;989&quot; height=&quot;493&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The August peak now makes sense as I took on a lot of &lt;a href=&quot;https://github.com/mdn/mdn/issues/432&quot;&gt;the Firefox 117 release tasks&lt;/a&gt; for CSS, HTTP, SVG, Web APIs, and DevTools changes.&lt;/p&gt;
&lt;p&gt;Highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Highlighting new color functions:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/25015&quot;&gt;Support for &lt;code&gt;color()&lt;/code&gt;, &lt;code&gt;lab()&lt;/code&gt;, &lt;code&gt;lch()&lt;/code&gt;, &lt;code&gt;oklab()&lt;/code&gt;, &lt;code&gt;oklch()&lt;/code&gt; in Firefox 117&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS examples made clearer:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/28618&quot;&gt;Adding examples for ‘auto none’ in CSS ‘contain-intrinsic-size’ property for Firefox 117&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;github-chores--maintenance&quot;&gt;GitHub chores &amp;amp; maintenance&lt;/h3&gt;
&lt;p&gt;If I was to categorize the ad-hoc or unplanned work I do, it lands under general maintenance and upkeep of the MDN GitHub organization.
The goal of this work is to keep our code repositories healthy and continuously improve the experience of contributing to MDN.&lt;/p&gt;
&lt;p&gt;This work typically involves improvements and fixes for CI and shared workflows (GitHub actions), improving Community Standards (READMEs, &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/MDN/Contribute&quot;&gt;contribution guidelines&lt;/a&gt;, etc.), and other admin work.&lt;/p&gt;
&lt;p&gt;Highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;mdn/content&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/28485&quot;&gt;fix(CI): reorder workflow steps, pass in .nvmrc config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mdn/mdn-contributor-spotlight&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/mdn-contributor-spotlight/pull/21&quot;&gt;chore: Add template docs and workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mdn/community-meetings&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/community-meetings/pull/15&quot;&gt;feat: Add minutes from 2023-12-11 call&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a wrap&lt;/h2&gt;
&lt;p&gt;We made it to the end of my 2023 wrap-up post: Thanks a lot for reading.
This was a great housekeeping task for me to see where my work lands and to course-correct for 2024.&lt;/p&gt;
&lt;p&gt;Let me know what your thoughts are, if I missed something, or if you have any other suggestions.
I haven’t made the raw PR stats available (although it’s all public data), but I’m happy to add them to this post if anyone is interested in taking a look for themselves.
Feel free to &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt; if you have any feedback or if you want to say hi.&lt;/p&gt;
</content:encoded><category>GitHub</category><category>MDN</category><category>Firefox</category><category>Mozilla</category></item><item><title>JavaScript console methods</title><link>https://bsmth.de/mdn-blog-javascript-console-methods/</link><guid isPermaLink="true">https://bsmth.de/mdn-blog-javascript-console-methods/</guid><description>I wrote a blog post on MDN where I cover some examples of what the JS console can do and some of the lesser-known methods that you might find helpful or just plain fun.</description><pubDate>Sun, 03 Dec 2023 07:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier in the year, &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/searching-code-with-grep/&quot;&gt;I wrote about using grep on the MDN Blog&lt;/a&gt; for code search under the “developer essential” category.
I categorize it this way because I think it will give people unfamiliar with grep a massive productivity boost and exposure to skills like working on the command line and regular expressions.
This looks like it’s becoming a series of posts on “essentials” (or what I think are incredibly beneficial skills, at least) for working effectively as a web developer.&lt;/p&gt;
&lt;p&gt;The next topic I thought would be interesting was JavaScript console methods.
People who work extensively with JavaScript might not even be aware that the console has capabilities that could eliminate the need to write custom handling or use third-party libraries.
What’s better than using built-in functionality that works everywhere to cut out unnecessary code?&lt;/p&gt;
&lt;p&gt;In the blog post, I covered some examples of what the console can do and some of the lesser-known methods that you might find helpful or just plain fun:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; dogs&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&quot;Yoshi&quot;&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;&quot;Black&quot;&lt;/span&gt;&lt;span&gt;, personality: &lt;/span&gt;&lt;span&gt;&quot;Calm&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&quot;Melanie&quot;&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;&quot;Brown&quot;&lt;/span&gt;&lt;span&gt;, personality: &lt;/span&gt;&lt;span&gt;&quot;Hyperactive&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  { name: &lt;/span&gt;&lt;span&gt;&quot;Peppers&quot;&lt;/span&gt;&lt;span&gt;, color: &lt;/span&gt;&lt;span&gt;&quot;white&quot;&lt;/span&gt;&lt;span&gt;, personality: &lt;/span&gt;&lt;span&gt;&quot;Unpredictable&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;table&lt;/span&gt;&lt;span&gt;(dogs);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/learn-javascript-console-methods/&quot;&gt;post I wrote for the MDN Blog&lt;/a&gt; that describes some fun and exciting console methods.
Everyone will find something new to use in projects, whether you’re a beginner or an advanced JavaScript developer.
Here’s what the post covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;:rotating_light: logging levels to indicate severity&lt;/li&gt;
&lt;li&gt;:100: counting events &amp;amp; adding timers for events or tasks&lt;/li&gt;
&lt;li&gt;:open_mouth: displaying data as tables&lt;/li&gt;
&lt;li&gt;:clipboard: grouping logs by topic, task, or event type&lt;/li&gt;
&lt;li&gt;:microscope: exploring application traces using the console&lt;/li&gt;
&lt;li&gt;:see_no_evil: clearing out the console if logs become noisy or distracting&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;warn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;You might not be subscribed to the MDN Blog RSS feed.&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; errorMsg&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; `Oops, we&apos;re on ${&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;} instead!`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// If the assertion is true, nothing happens&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;console.&lt;/span&gt;&lt;span&gt;assert&lt;/span&gt;&lt;span&gt;(document.location &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &quot;https://bsmth.de&quot;&lt;/span&gt;&lt;span&gt;, { errorMsg });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Assertion failed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Object { errorMsg: &quot;Oops, we&apos;re on https://bsmth.de/mdn-blog-javascript-regex-reference/ instead&quot; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you liked the post, you should also see the &lt;a href=&quot;https://bsmth.de/mdn-blog-javascript-regex-reference/&quot;&gt;JavaScript regular expression&lt;/a&gt; updates.
Let me know what you think about the MDN blog post and if you use grep for something fun.
Leave a comment below or &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;console.info(&quot;Happy hacking!&quot;)&lt;/code&gt; :computer:&lt;/p&gt;
</content:encoded><category>JavaScript</category><category>MDN</category><category>Baseline</category></item><item><title>Learn grep for powerful code search</title><link>https://bsmth.de/mdn-blog-terminal-code-search-grep/</link><guid isPermaLink="true">https://bsmth.de/mdn-blog-terminal-code-search-grep/</guid><description>I wrote a blog post on MDN about grep, which covers what it is and how to use it, and why I think learning grep will give you a significant skills and productivity boost.</description><pubDate>Mon, 10 Jul 2023 05:30:00 GMT</pubDate><content:encoded>&lt;p&gt;A big part of web development extends beyond the usual suspects of HTML, CSS, JavaScript, and HTTP.
You need to glue these building blocks together, and for that, you need to be able to use tools, systems, and other fundamentals.&lt;/p&gt;
&lt;p&gt;Aside from soft skills, understanding how files and file systems work, using shell scripting languages, working with IDEs, and managing the actual code are developer essentials.&lt;/p&gt;
&lt;p&gt;I think that search is one of the most important skills when it comes to working with code.
I make searches using grep many times a day to look for all kinds of patterns in code and documentation.
If I find a typo in a document on MDN, I’ll use grep to search all of MDN Web Docs to see if there are others like it or if it’s copy-pasted elsewhere.
For code, I’ll look where variables are defined in a project, error messages in logs, patterns that are deprecated or need to be refactored, and so on.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# find &quot;the the &quot; repeated word typo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; &quot;the the &quot;&lt;/span&gt;&lt;span&gt; files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# path/to/index.md: and upper limit or the the keyword `auto`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# find legacy rgba() CSS function, ignore node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; --exclude-dir=node_modules&lt;/span&gt;&lt;span&gt; &quot;rgba(&quot;&lt;/span&gt;&lt;span&gt; ./&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# ./files/path/to/index.md:  background: rgba(0, 0, 0, 0.3);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The post I wrote for the MDN blog will get you started with interesting and fun examples to try.
Check it out if you’d like to read about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what grep is and how to use it&lt;/li&gt;
&lt;li&gt;piping commands into grep&lt;/li&gt;
&lt;li&gt;some real-world timings to show how fast it is compared to alternatives&lt;/li&gt;
&lt;li&gt;why I think learning grep will give you a significant skills and productivity boost&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# time how long it takes to grep for a pattern and&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# perform a line count on the output&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;time&lt;/span&gt;&lt;span&gt; grep -r &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\`\`\`&lt;/span&gt;&lt;span&gt;plain&quot;&lt;/span&gt;&lt;span&gt; files/en-us/web/ &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; wc&lt;/span&gt;&lt;span&gt; -l&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     252&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt;  0.43s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 0.29s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 99%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 0.730&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;wc&lt;/span&gt;&lt;span&gt; -l&lt;/span&gt;&lt;span&gt;  0.00s&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;span&gt; 0.00s&lt;/span&gt;&lt;span&gt; system&lt;/span&gt;&lt;span&gt; 0%&lt;/span&gt;&lt;span&gt; cpu&lt;/span&gt;&lt;span&gt; 0.729&lt;/span&gt;&lt;span&gt; total&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you liked the post, you should also see the &lt;a href=&quot;https://bsmth.de/mdn-blog-javascript-regex-reference/&quot;&gt;JavaScript regular expression&lt;/a&gt; updates.
Let me know what you think about the MDN blog post and if you use grep for something fun.
Leave a comment below or &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy &lt;code&gt;/(grep|hack)ing)/&lt;/code&gt; :mag:&lt;/p&gt;
</content:encoded><category>Command Line</category><category>MDN</category></item><item><title>CSS hues with hsl()</title><link>https://bsmth.de/mdn-blog-hues-using-hsl/</link><guid isPermaLink="true">https://bsmth.de/mdn-blog-hues-using-hsl/</guid><description>This post describes what hues are, why you might use them, and other practical hints to get started with hue() in CSS.</description><pubDate>Fri, 30 Jun 2023 08:33:16 GMT</pubDate><content:encoded>&lt;p&gt;After deep-diving into color functions like &lt;code&gt;lab()&lt;/code&gt;, &lt;code&gt;lch()&lt;/code&gt; and &lt;code&gt;color()&lt;/code&gt;, I realized that hue was actually a great topic to update across MDN Web Docs.
I am pretty happy with the outcome and there was a lot of interesting findings, updated pages, and new examples that should help beginners get started using hues in CSS.&lt;/p&gt;


&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#color-wheel&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  border-radius&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;conic-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    in&lt;/span&gt;&lt;span&gt; hsl longer &lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    hsl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    hsl&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;360&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/* For browsers that don&amp;#39;t support hue interpolation methods */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@supports&lt;/span&gt;&lt;span&gt; not&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;conic-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; hsl longer &lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  #color-wheel&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;conic-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;yellow&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;green&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;cyan&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;blue&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;magenta&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;If you want to learn about hue in CSS, the &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/learn-css-hues-colors-hsl/&quot;&gt;post on the MDN Blog&lt;/a&gt; describes what hues are, why you might use them, and other practical hints to get started.
Check it out if you’d like to read about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what hues are and why they’re useful&lt;/li&gt;
&lt;li&gt;how to use &lt;code&gt;hsl()&lt;/code&gt; including:
&lt;ul&gt;
&lt;li&gt;the basics of how &lt;code&gt;hsl()&lt;/code&gt; works&lt;/li&gt;
&lt;li&gt;playing with hues in CSS&lt;/li&gt;
&lt;li&gt;seeing how to control saturation and lightness&lt;/li&gt;
&lt;li&gt;using variables to change hues in multiple places&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;mdn-web-docs-hue-updates&quot;&gt;MDN Web Docs hue updates&lt;/h2&gt;
&lt;p&gt;If you want to see the specific PRs I worked on (mostly relating to CSS Colors Module Level 4) here’s the GitHub links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hue:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/25350&quot;&gt;docs(CSS): add examples of hue interpolation methods, add hue page&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fx111:&lt;/strong&gt; color(), lab(), lch(), oklab(), oklch() support
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdn/content/pull/25015&quot;&gt;MDN Content&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://github.com/mdn/browser-compat-data/pull/19035&quot;&gt;Browser Compat Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fx113:&lt;/strong&gt; CSS Color lvl 4 features are enabled by default
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mdn/content/pull/26298&quot;&gt;MDN Content&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://github.com/mdn/browser-compat-data/pull/19426&quot;&gt;Browser Compat Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should see plenty more support information and lots more colorful examples on MDN that I hope inspire readers:&lt;/p&gt;

&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; container&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; loopLength&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 80&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Loop to create 40 div elements&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; i &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;; i &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; loopLength; i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; divElement&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;createElement&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;div&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  divElement.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; `hsl-${&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Calculate the background color based on the loop index&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; hue&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Math.&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;((i &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; loopLength) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 360&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; saturation&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; lightness&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  divElement.textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; `hsl(${&lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;} ${&lt;/span&gt;&lt;span&gt;saturation&lt;/span&gt;&lt;span&gt;} ${&lt;/span&gt;&lt;span&gt;lightness&lt;/span&gt;&lt;span&gt;})`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  divElement.style.backgroundColor &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; `hsl(${&lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;}, ${&lt;/span&gt;&lt;span&gt;saturation&lt;/span&gt;&lt;span&gt;}%, ${&lt;/span&gt;&lt;span&gt;lightness&lt;/span&gt;&lt;span&gt;}%)`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // Append the div to the container&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  container.&lt;/span&gt;&lt;span&gt;appendChild&lt;/span&gt;&lt;span&gt;(divElement);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;I already mentioned in the MDN blog post, but I’d like to give a shout out to &lt;a href=&quot;https://www.github.com/yarusome&quot;&gt;Yarusome&lt;/a&gt; who did a really nice job adding diagrams that explain hue interpolation methods. Big thanks for adding more colors :pray:&lt;/p&gt;
&lt;p&gt;Let me know what your thoughts are, what I missed, and if there are some fun ways to use these new features.
Share your thoughts below or &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>CSS</category><category>MDN</category><category>Baseline</category></item><item><title>JavaScript regular expressions reference docs</title><link>https://bsmth.de/mdn-blog-javascript-regex-reference/</link><guid isPermaLink="true">https://bsmth.de/mdn-blog-javascript-regex-reference/</guid><description>I worked with Josh-Cena to create new pages for each JavaScript regex language feature, with information about the syntax and browser compatibility data.</description><pubDate>Wed, 24 May 2023 06:31:11 GMT</pubDate><content:encoded>&lt;p&gt;The JavaScript regular expressions (regex) documentation had a big update recently thanks to the efforts of &lt;a href=&quot;https://github.com/Josh-Cena&quot;&gt;Joshua Chen&lt;/a&gt;.
Josh did the heavy lifting, and I was glad to give some feedback and reviews to land the changes.
There are new, dedicated pages for each JS language feature for regular expressions, with information about the syntax and browser compat data, too.&lt;/p&gt;
&lt;h2 id=&quot;mdn-blog-javascript-regular-expressions&quot;&gt;MDN Blog, JavaScript regular expressions&lt;/h2&gt;
&lt;p&gt;If you want to see everything that’s changed, we &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/regular-expressions-reference-updates/&quot;&gt;published a post on the MDN Blog&lt;/a&gt; that covers highlights and specifics of what’s been updated.
Check it out if you want to read the guides for creating regular expressions in JavaScript and other pages that cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;flags that change how a regex is interpreted&lt;/li&gt;
&lt;li&gt;assertions to check if a pattern matches a condition (e.g., start of a string)&lt;/li&gt;
&lt;li&gt;atoms - the units that make up a regex pattern&lt;/li&gt;
&lt;li&gt;other features like quantifiers (&lt;code&gt;*&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt; etc.) that help you compose patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Out of the examples, I highlighted a few in the blog post that I thought were nice and are useful for certain purposes.
The one below is nice because it detects strings in specific &lt;a href=&quot;https://unicode.org/iso15924/iso15924-codes.html&quot;&gt;scripts or writing systems&lt;/a&gt; without having to exhaustively specify the characters or ranges of characters that make a script:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; mixedCharacters&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &amp;quot;aεЛ&amp;quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// I could write out everything ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// mixedCharacters.match(/[ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλ... &amp;lt;-- oh no&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Or use a short alias for Greek instead:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mixedCharacters.&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;\p&lt;/span&gt;&lt;span&gt;{Script=Grek}/&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// ε&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// Or a short name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mixedCharacters.&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;\p&lt;/span&gt;&lt;span&gt;{sc=Cyrillic}/&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;// Л&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;github-prs-updating-js-regex&quot;&gt;GitHub PRs updating JS regex&lt;/h2&gt;
&lt;p&gt;Here’s the two main GitHub PRs that we worked on to add the updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MDN Content:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/22210&quot;&gt;Regex reference (by Josh Cena)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser Compat Data:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/browser-compat-data/pull/18966&quot;&gt;feat(JS): Add JS regex patterns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the Browser Compat Data, I had to cover a lot of cases across multiple browsers and operating systems.
I did this using &lt;a href=&quot;https://www.browserstack.com/&quot;&gt;Browserstack&lt;/a&gt; with a codepen like the one below so I could visually check for matches as a kind of smoke test.
Of course, you could do &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test&quot;&gt;asserts with test()&lt;/a&gt; and automate this headlessly, but this was the quickest way to check for passing cases and fill in the blanks.
Sometimes simply brute-forcing the permutations of a feature is the most foolproof way:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;backreference&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;backreference: &amp;quot;oneone&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;capturingGroup&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;capturing group: &amp;quot;capturing group&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;characterClass&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;character class: &amp;quot;tuv xyz&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;characterClassEscape&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;character class escape: &amp;quot;1&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;characterEscape&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;character escape: &amp;quot;&lt;/span&gt;&lt;span&gt;&amp;amp;#9;&lt;/span&gt;&lt;span&gt;&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;disjunction&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;disjunction: &amp;quot;b&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;inputBoundaryAssertion&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;input boundary assertion&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;lookaheadAssertion&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;lookahead assertion: &amp;quot;one two three&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;lookbehindAssertion&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;lookbehind assertion: &amp;quot;one two three&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;namedBackreference&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;named backreference: &amp;quot;**bold**&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;namedCapturingGroup&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;named capturing group: &amp;quot;**bold**&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;nonCapturingGroup&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;non-capturing group: &amp;quot;https://example.com&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;quantifier&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;quantifier: &amp;quot;www&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;unicodeCharacterClassEscape&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;unicode character class escape: &amp;quot;🍕&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;wildcard&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;wildcard: &amp;quot;a&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;wordBoundaryAssertion&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;boundary assertion: &amp;quot;word&amp;quot;&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;ol&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; backreference&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(one)&lt;/span&gt;&lt;span&gt;\1&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; capturingGroup&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(capturing group)/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; characterClass&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(&lt;/span&gt;&lt;span&gt;[xyz]&lt;/span&gt;&lt;span&gt;)/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; characterClassEscape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; characterEscape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\t&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; disjunction&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /a&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;b/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; inputBoundaryAssertion&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;input boundary assertion&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;)/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; lookaheadAssertion&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /one(?= two)/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; lookbehindAssertion&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(?&amp;lt;=one )two/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; namedBackreference&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(?&amp;lt;&lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;\*\*&lt;/span&gt;&lt;span&gt;)(?&amp;lt;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;*?&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\k&amp;lt;&lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; namedCapturingGroup&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(?&amp;lt;&lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;\*\*&lt;/span&gt;&lt;span&gt;)(?&amp;lt;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;*?&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;\k&amp;lt;&lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; nonCapturingGroup&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /(?:https&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\/\/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;example&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;com/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; quantifier&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /w&lt;/span&gt;&lt;span&gt;{3}&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; unicodeCharacterClassEscape&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\p&lt;/span&gt;&lt;span&gt;{Emoji_Presentation}/&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; wildcard&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&amp;quot;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&amp;quot;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; wordBoundaryAssertion&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;\b&lt;/span&gt;&lt;span&gt;word&lt;/span&gt;&lt;span&gt;\b&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; allRegexFeatures&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  backreference,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  capturingGroup,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  characterClass,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  characterClassEscape,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  characterEscape,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  disjunction,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  inputBoundaryAssertion,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  lookaheadAssertion,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  lookbehindAssertion,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  namedBackreference,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  namedCapturingGroup,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  nonCapturingGroup,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  quantifier,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  unicodeCharacterClassEscape,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  wildcard,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  wordBoundaryAssertion,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// get all list items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; listItems&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;quot;li&amp;quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// loop through all list items&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;listItems.&lt;/span&gt;&lt;span&gt;forEach&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; item.id;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // get the regex feature from the JSON dict&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; regexFeature&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; allRegexFeatures[id];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; item.textContent;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // match the regex feature to the text content&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; match&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; text.&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(regexFeature);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  // if there is a match, highlight the match&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; (match) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    item.innerHTML &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; text.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      match[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      `&amp;lt;span class=&amp;quot;match&amp;quot;&amp;gt;${&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&amp;lt;/span&amp;gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  font-family&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;monospace&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.match&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cornflowerblue&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  font-weight&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;If you’re getting started with JavaScript and regular expressions, I hope these resources make the topic a little less scary.
Let me know what you think or if I missed anything below or &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.
Happy &lt;code&gt;/pattern|matching/&lt;/code&gt;!&lt;/p&gt;</content:encoded><category>JavaScript</category><category>MDN</category></item><item><title>CSS color functions and color spaces</title><link>https://bsmth.de/mdn-blog-css-colors-hues-functions/</link><guid isPermaLink="true">https://bsmth.de/mdn-blog-css-colors-hues-functions/</guid><description>I wrote docs and a blog post on MDN about color in CSS, color spaces, hues, interpolation, and other interesting topics.</description><pubDate>Fri, 05 May 2023 08:33:16 GMT</pubDate><content:encoded>&lt;p&gt;In March of this year, I was assigned a &lt;a href=&quot;https://github.com/mdn/content/issues/24395&quot;&gt;Firefox 111 release task&lt;/a&gt; to document &lt;code&gt;lab()&lt;/code&gt;, &lt;code&gt;lch()&lt;/code&gt; and &lt;code&gt;color()&lt;/code&gt; functions that were shipping in the stable release.
This was an interesting one for me to work on and I learned a lot of new things about color spaces, color science, and plenty about color in CSS.&lt;/p&gt;
&lt;p&gt;It turns out that color in CSS is a pretty deep topic, and there’s lots of options for declaring and controlling colors that can improve how you code, and how your projects look (including better gradients):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;hsl-named-longer&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;HSL linear gradient&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&amp;quot;lch-named-longer&amp;quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;LCH linear gradient&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.hsl-named-longer&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; right&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; hsl longer &lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;orange&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;yellow&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.lch-named-longer&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;linear-gradient&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; right&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; lch longer &lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;orange&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;yellow&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There&apos;s a live preview of this example on the site.&lt;/p&gt;
&lt;p&gt;If you want to see the docs I updated, here’s the links to the GitHub PRs I worked on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MDN Content:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/content/pull/25015&quot;&gt;Relnote, examples, and docs for color functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser Compat Data:&lt;/strong&gt; &lt;a href=&quot;https://github.com/mdn/browser-compat-data/pull/19035&quot;&gt;relnote(Fx111): color(), lab(), lch(), oklab(), oklch() support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It should be apparent that there’s plenty of new ways to specify colors:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.more-color-functions&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;hwb&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;194&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* HWB (Hue, Whiteness, Blackness) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;lab&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt; 59.5&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* LAB (Lightness, A-axis, B-axis) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;lch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;52.2&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 72.2&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* LCH (Lightness, Chroma, Hue) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;oklab&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;59&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 0.1&lt;/span&gt;&lt;span&gt; 0.1&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* Oklab (Lightness, A-axis, B-axis) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;oklch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 0.15&lt;/span&gt;&lt;span&gt; 50&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* Oklch (Lightness, Chroma, Hue) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;mdn-blog-css-colors-module-4&quot;&gt;MDN Blog, CSS Colors (Module 4)&lt;/h2&gt;
&lt;p&gt;If you want to read more about CSS Colors Module Level 4, I &lt;a href=&quot;https://developer.mozilla.org/en-US/blog/css-color-module-level-4/&quot;&gt;published a post on the MDN Blog&lt;/a&gt; that covers color spaces, hues, interpolation, and other interesting topics.
Check it out if you’d like to read about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;defining RGB colors in CSS&lt;/li&gt;
&lt;li&gt;understanding color spaces&lt;/li&gt;
&lt;li&gt;using HSL for choosing hues&lt;/li&gt;
&lt;li&gt;what’s new in CSS Colors Module Level 4:
&lt;ul&gt;
&lt;li&gt;color spaces and gamuts&lt;/li&gt;
&lt;li&gt;new color functions (functional notation)&lt;/li&gt;
&lt;li&gt;syntax changes for existing color functions&lt;/li&gt;
&lt;li&gt;how gradients in different color spaces&lt;/li&gt;
&lt;li&gt;hue interpolation modes in gradients&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let me know what your thoughts are, what I missed, and if there are some fun ways to use these new features.
Share your thoughts below or &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;get in touch on Bluesky&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>CSS</category><category>MDN</category><category>Baseline</category></item><item><title>From Google Analytics to Netlify Analytics</title><link>https://bsmth.de/migrating-from-google-to-netlify-analytics/</link><guid isPermaLink="true">https://bsmth.de/migrating-from-google-to-netlify-analytics/</guid><description>This post covers my experience switching over to Netlify Analytics from Google Analytics, my initial impressions, and what I think the benefits are.</description><pubDate>Sat, 27 Aug 2022 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I decided to make the change recently to try out Netlify Analytics in place of Google Analytics. I was initially motivated to improve my website’s performance and eliminate trackers, but I’m also curious about how it is from the maintainer side. In this post, I’ll describe my motivations, what I did to get started, my initial impressions, and things I learned along the way.&lt;/p&gt;
&lt;h2 id=&quot;what-is-netlify-analytics&quot;&gt;What is Netlify Analytics?&lt;/h2&gt;
&lt;p&gt;Netlify is a cloud development platform for building and deploying websites with other serverless features available on top. In terms of where the company is, they’ve &lt;a href=&quot;https://hostadvice.com/news/netlify-raises-105-million-in-a-series-d-round-led-by-bessemer-venture-partners/&quot;&gt;raised $212M&lt;/a&gt; in VC funding, they employ around 200 people and operate at &lt;a href=&quot;https://getlatka.com/companies/netlify&quot;&gt;~$23M in annual revenue&lt;/a&gt;. The platform has great integrations, notably GitHub compatibility which allows you to easily connect code repositories and rebuild on pull requests, for example. Overall, I find it very convenient to use because of low-friction workflows for previewing incoming changes and quickly deploying.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/dalle-migrating-analytics-netlify-cyberpunk.png&quot; alt=&quot;A cyberpunk style image depicting Netlify analytics generated by DALL·E mini.&quot; title=&quot;What DALL·E mini thinks cyberpunk Netlify Analytics looks like.&quot; width=&quot;1600&quot; height=&quot;900&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;In 2019, Netlify &lt;strong&gt;launched an Analytics service&lt;/strong&gt; that helps you understand visits to web pages deployed on their platform. Because Netlify run the entire edge infrastructure they use to serve websites and applications, it can provide insights metrics directly from their infrastructure logs.&lt;/p&gt;
&lt;h2 id=&quot;whats-the-benefit-of-netlify-analytics&quot;&gt;What’s the benefit of Netlify Analytics?&lt;/h2&gt;
&lt;p&gt;Insights and analytics are the best ways to understand who is visiting your website and what types of content is being viewed. The problem with Google Analytics is that there are clear privacy concerns with the amount and type of user data being collected, what is being done with it, who owns it and for how long.&lt;/p&gt;
&lt;p&gt;There are many privacy-focused analytics tools available that offer better handling and control of user data. &lt;a href=&quot;https://matomo.org/&quot;&gt;Matomo&lt;/a&gt;, &lt;a href=&quot;https://plausible.io/&quot;&gt;Plausible&lt;/a&gt;, and &lt;a href=&quot;https://usefathom.com/&quot;&gt;Fathom&lt;/a&gt; are some of the most popular Google Analytics alternatives with a focus on privacy. The catch with all of these tools is they load client-side on the user’s browser when they visit your website and you still end up with a tracker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Client-side tracking&lt;/strong&gt; opens the door for device and user fingerprinting and other bad behavior. The number of people who use ad blockers is also growing year over year, completely eliminating the ability for trackers to collect user session data. If you run a technical blog, it would be safe to assume that over half of your readers have ad blockers installed. Privacy-focused analytics tools are a step in the right direction, but maybe user tracking can be fully avoided without sacrificing visibility on website analytics.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The bandwidth needed&lt;/strong&gt; to load the analytics snippets results in a big performance hit. Google Analytics is added via a script which fetches tracking code from Google’s servers. The code that’s being run at page load can change dynamically depending on the types of data collection active. In my experience of using Google Analytics over the years, it seems that the footprint of the tracking code itself has increased considerably.&lt;/p&gt;
&lt;h2 id=&quot;measuring-web-performance-with-lighthouse&quot;&gt;Measuring web performance with lighthouse&lt;/h2&gt;
&lt;p&gt;To measure user experience, I turned to &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse&quot;&gt;Chrome’s lighthouse&lt;/a&gt; for page speed tests. I found their treemap to be a great way of inspecting and visualizing resource sizes beyond inspecting network requests in dev tools. This screenshot shows our baseline, with the JS resources by size from my home page as visualized in a treemap:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/treemap-pre.png&quot; alt=&quot;The lighthouse treemap view of bsmth.de highlighting Google Analytics&quot; title=&quot;Size of JS resources by area and bundle&quot; width=&quot;3081&quot; height=&quot;1335&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;There’s a couple of things we can take away from this screenshot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;589 KB:&lt;/strong&gt; the total size of JS resources&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;202 KB:&lt;/strong&gt; the largest resource served is Google Analytics (top-right, blue rectangle)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 visibility&lt;/strong&gt; into what the GA script/tag contains&lt;/li&gt;
&lt;li&gt;All of the other resources look more-or-less essential, to be inspected later&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;migrating-from-google-analytics-to-netlify-analytics&quot;&gt;Migrating from Google Analytics to Netlify Analytics&lt;/h2&gt;
&lt;p&gt;There’s not much that has to be done to enable Analytics on the Netlify admin panel. Analytics is &lt;strong&gt;not part of their free plan&lt;/strong&gt;, but it can be enabled as a single addon for your account &lt;a href=&quot;https://www.netlify.com/pricing/&quot;&gt;starting at $9 USD per month&lt;/a&gt;. After it’s enabled, there’s an &lt;strong&gt;Analytics&lt;/strong&gt; tab in the top right corner of the Netlify admin panel.&lt;/p&gt;
&lt;p&gt;The first view that you get is some basic metrics of pageviews over time:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/netlify-analytics-example.png&quot; alt=&quot;The Analytics panel in Netlify showing pageviews&quot; title=&quot;Some basic metrics from the Analytics view.&quot; width=&quot;2481&quot; height=&quot;1604&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;One thing you get from Netlify Analytics that you don’t get from client-side tracking is the top pages and resources not found. If we’re running our own server or cloud deploy, we would be able to see all network requests with much finer detail, but we’re balancing that with the simplicity of a completely managed service with Netlify.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/resources-not-found.png&quot; alt=&quot;Netlify Analytics panel showing top pages and resources not found&quot; width=&quot;2435&quot; height=&quot;1871&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;From the resources not found panel, there’s a few things we can take away:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bots&lt;/strong&gt; seem to love looking for wordpress resources and logins&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; is what bots look for nowadays, showing how common it is for environment vars &amp;amp; secrets to land on the public internet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TODO:&lt;/strong&gt; I need to set up redirects to new URLs since I redesigned the site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After Netlify’s Analytics were up and running, I removed the Google Analytics script from my pages and deployed to inspect the results. Here’s a look at the treemap view again after the migration:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/treemap-post.png&quot; alt=&quot;The lighthouse treemap view after Google Analytics is removed&quot; width=&quot;3083&quot; height=&quot;1340&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;We’ve managed to achieve:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;202 KB:&lt;/strong&gt; savings after Google Analytics is removed :white_check_mark:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;~35%&lt;/strong&gt; decrease in page size :white_check_mark:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100% visibility&lt;/strong&gt; and control over the content with no third-party tracking :white_check_mark:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Incidentally, Chrome’s treemap has an &lt;code&gt;Unused bytes&lt;/code&gt; view which shows that considerable parts of the production bundle is unused, but I have no idea what ‘unused’ means in this context or how to interpret the view. If you have some experience using this view, feel free to comment below with hints.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://bsmth.de/images/august-2022/treemap-post-post.png&quot; alt=&quot;The lighthouse treemap showing unused code&quot; width=&quot;3089&quot; height=&quot;1338&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2 id=&quot;first-impressions-of-netlify-analytics&quot;&gt;First impressions of Netlify Analytics&lt;/h2&gt;
&lt;p&gt;I like that I can get analytics inside a tool and admin panel that I’m already using for my ‘production deploys’. The performance benefits are great, with much faster page load times, fewer HTTP requests, and boiling less of the ocean in order to show cats with orbiting emojis. I also like that I can see what resources are missing, that helps with adding redirects for changed URLs.&lt;/p&gt;
&lt;p&gt;The details of the analytics are not at the level you would get from Google Analytics and I was hoping for a bit more from a paid plan. I cannot create custom views, funnels, drill-down queries or any other standard view, but I think I have &lt;strong&gt;just enough information&lt;/strong&gt; to make good choices.&lt;/p&gt;
&lt;p&gt;The bot traffic is obviously not great that it is mixed in with real traffic and cannot be filtered out, but you are measuring visits from users that would otherwise be missing by the use of ad blockers. In this sense, we’re compensating in the other direction of having too much traffic shown than is realistic, but it has to be kept in mind.&lt;/p&gt;
&lt;p&gt;I use this blog as a testbed for web development and I’m always looking to try out new tools to build, deploy and maintain websites. At first, I though $9 USD per month for very basic analytics was a bit steep, but it’s worth it for initial experimentation and the ability to share my experience. My wallet will take a hit for the sake of your privacy here.&lt;/p&gt;
&lt;p&gt;My post-purchase rationalization is this: if you consider what you get with Netlify’s free plan with the amount of functionality that comes with it, then free plan, build minutes, previews, user management &amp;amp; analytics is a good deal.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I have turned the switch from Google Analytics to Netlify Analytics as of a few hours ago, so I will likely revisit this topic in another post in future to see how the experience is longer-term. I’m happy with the results so far, and glad to have a site that is tracker-free, is privacy-friendly and is a bit more resource-efficient.&lt;/p&gt;
&lt;p&gt;Thanks for reading! If you have any questions, comments, or suggestions, drop them below.&lt;/p&gt;
</content:encoded><category>OSS</category></item><item><title>Jekyll, Amplify, GitHub actions deploys</title><link>https://bsmth.de/deploying-jekyll-github-actions-aws-amplify/</link><guid isPermaLink="true">https://bsmth.de/deploying-jekyll-github-actions-aws-amplify/</guid><description>A hacked Wordpress install left me looking for better alternatives. If you want to try static pages, here&apos;s how to quickly deploy to AWS Amplify via GitHub.</description><pubDate>Thu, 10 Feb 2022 08:33:16 GMT</pubDate><content:encoded>&lt;p&gt;One tech stack I like is using Jekyll to build static pages with vanilla HTML &amp;amp; CSS, AWS Amplify for deployments, and GitHub actions to pick up broken HTML. This entire setup is designed to be as low-maintenance as possible while remaining customizable and configurable. There’s a few benefits of using Amplify instead of GitHub pages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatically deploy after commits to your main branch&lt;/li&gt;
&lt;li&gt;Password protect pages (or branches)&lt;/li&gt;
&lt;li&gt;Domain management via Route 53&lt;/li&gt;
&lt;li&gt;Private GitHub repository support&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;why-wordpress-can-be-insecure&quot;&gt;Why Wordpress can be insecure&lt;/h2&gt;
&lt;p&gt;The first website I built in the mid ’90s was a simple collection of static HTML pages I used for sharing audio files.
I don’t remember what kind of tooling I used to build it, but I think it involved FTP and probably had a lot of handwritten HTML.
Cool. 😐&lt;/p&gt;
&lt;p&gt;I started to use WordPress around 2005, which was a big improvement at first.
I learned a lot by digging into PHP, JavaScript, managing a server and learning how SEO works. WordPress ended up being my go-to platform for about a decade right up until the point I got &lt;strong&gt;mercilessly hacked into oblivion&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;After a thorough postmortem of the hacking incident, I narrowed the cause down to two possibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An advanced persistent threat conducted a targeted cyber attack and pivoted laterally through my mainframe&lt;/li&gt;
&lt;li&gt;Someone automated a &lt;a href=&quot;https://github.com/wpscanteam/wpscan&quot;&gt;tool like WPScan&lt;/a&gt; to look for insecure WP sites and walked in the back door&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The sprawling ecosystem of rarely maintained PHP and JavaScript plugins&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; written by third parties makes it super simple to install unaudited code on your server. The end result for unlucky WP users is that your website gets PWNd after installing a responsive image carousel.&lt;/p&gt;
&lt;h2 id=&quot;why-jekyll-is-great-for-building-static-websites&quot;&gt;Why Jekyll is great for building static websites&lt;/h2&gt;
&lt;p&gt;I noticed &lt;a href=&quot;https://www.jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt; was being used on a lot of websites and developer documentation. I like to try out new tools quite often, so I decided to take a look for myself. The benefits started to become really obvious after a couple of days of using it, but I really enjoy it for these reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No server-side&lt;/strong&gt; complexity to serve pages or maintain&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt; for your pages and blog content&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://shopify.github.io/liquid/&quot;&gt;Liquid&lt;/a&gt; HTML templating language&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt; build times &amp;amp; &lt;strong&gt;fast&lt;/strong&gt; page load speed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ruby&lt;/strong&gt; is fun and easy to learn&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m very happy with this setup right now as I can truly focus on the content like so many platforms and frameworks promise but never really deliver.&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-jekyll&quot;&gt;Getting started with Jekyll&lt;/h2&gt;
&lt;p&gt;To get started with this stack, &lt;a href=&quot;https://github.com/new&quot;&gt;create a new GitHub repository&lt;/a&gt; with the name &lt;strong&gt;awesome_jekyll_site&lt;/strong&gt; (this can also be a private repository). The project setup should look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; jekyll&lt;/span&gt;&lt;span&gt; bundler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; clone&lt;/span&gt;&lt;span&gt; git@github.com:{your_gh_user}/awesome_jekyll_site.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jekyll&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; awesome_jekyll_site&lt;/span&gt;&lt;span&gt; --force&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; awesome_jekyll_site&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; exec&lt;/span&gt;&lt;span&gt; jekyll&lt;/span&gt;&lt;span&gt; s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A server is now running locally with your awesome new pages on &lt;code&gt;http://127.0.0.1:4000&lt;/code&gt;:&lt;/p&gt;
&lt;h2 id=&quot;deploying-jekyll-pages-to-aws-amplify&quot;&gt;Deploying Jekyll pages to AWS Amplify&lt;/h2&gt;
&lt;p&gt;In the AWS console, select the &lt;a href=&quot;https://eu-west-1.console.aws.amazon.com/amplify&quot;&gt;Amplify&lt;/a&gt; service and click &lt;strong&gt;New App&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Host web app&lt;/strong&gt;. In the “From your existing code” section, select &lt;strong&gt;GitHub&lt;/strong&gt; and click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Grant AWS Amplify access to your GitHub repository and choose the &lt;strong&gt;awesome_jekyll_site&lt;/strong&gt; repo. Keep the &lt;strong&gt;main&lt;/strong&gt; branch as the default and click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Keep the default build configuration and click &lt;strong&gt;Next&lt;/strong&gt;, review your settings and click &lt;strong&gt;Save and deploy&lt;/strong&gt;. Your new site will be publicly visible at a URL in the following format:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://{branchname}.{app_id}.amplifyapp.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Adding a domain to these pages is simple, especially if you already have one registered via Route 53. In your Amplify app, navigate to &lt;strong&gt;Domain management&lt;/strong&gt; and click &lt;strong&gt;Add domain&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I’m already using &lt;code&gt;bsmth.de&lt;/code&gt; so I am excluding the root and adding my Jekyll demo pages as a subdomain. Once you’ve chosen your awesome domain, click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;adding-github-actions-to-deployments-ftw&quot;&gt;Adding GitHub actions to deployments FTW&lt;/h2&gt;
&lt;p&gt;At this point, we have a simple Jekyll blog with automated deploys, and a custom domain with SSL certs applied. We’re in a good place in terms of keeping maintenance to a minimum, but there’s a great workflow improvement I use to catch errors before they land on public pages.&lt;/p&gt;
&lt;p&gt;For this step, we’ll use a GitHub action with the fantastic Ruby gem &lt;a href=&quot;https://github.com/gjtorikian/html-proofer&quot;&gt;html-proofer&lt;/a&gt;. This linter catches broken links (internal and external), missing alt tags for images, malformed HTML and more.&lt;/p&gt;
&lt;p&gt;On GitHub, navigate to your &lt;strong&gt;awesome_jekyll_site&lt;/strong&gt; repository, click &lt;strong&gt;Actions&lt;/strong&gt; and select &lt;strong&gt;New workflow&lt;/strong&gt;. Click &lt;strong&gt;set up a workflow yourself&lt;/strong&gt; and paste the following YAML:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark-default&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Check HTML in PRs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  pull_request&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    branches&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  # Allows the workflow to be run from the Actions tab manually&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  workflow_dispatch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  jekyll&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-16.04&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🛎 Check out master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          fetch-depth&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🚀 Cache deps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/cache@v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          path&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;vendor/bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          key&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ runner.os }}-gems-${{ hashFiles(&apos;**/Gemfile.lock&apos;) }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          restore-keys&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ${{ runner.os }}-gems-${{ hashFiles(&apos;**/Gemfile.lock&apos;) }}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;🔨 Build site&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;lemonarc/jekyll-action@1.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;📉 Test HTML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;chabad360/htmlproofer@master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          directory&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;./_site&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          arguments&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            --assume-extension&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            --url-ignore &quot;/example.com|twitter.com/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            --enforce_https&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            --check-html&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            --check_opengraph&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Click &lt;strong&gt;Start commit&lt;/strong&gt; with the changes on a new branch which opens a pull request. A CI job will start and check if we have any dead or broken links.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The first CI run will take a few minutes to complete, but once Ruby dependencies are cached, the action takes around 45 seconds to build and test your website.&lt;/p&gt;
&lt;p&gt;In this article we’ve learned how to create a basic Jekyll blog, deploy it using AWS Amplify with a domain from Route 53, and added a GitHub action that catches errors on new pull requests.&lt;/p&gt;
&lt;p&gt;I hope you found it useful and if you end up building something using this stack, &lt;a href=&quot;https://bsky.app/profile/bsmth.de&quot;&gt;let me know on Bluesky&lt;/a&gt;.&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;There are &lt;a href=&quot;https://wordpress.org/plugins/&quot;&gt;over 55,000 WordPress plugins&lt;/a&gt; available as of January 2020 &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded><category>OSS</category></item></channel></rss>