<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Bram.us</title>
	<atom:link href="https://www.bram.us/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.bram.us</link>
	<description>A rather geeky/technical weblog, est. 2001, by Bramus</description>
	<lastBuildDate>Thu, 09 Apr 2026 13:59:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Introducing view-transitions-toolkit, a collection of utility functions to more easily work with View Transitions.</title>
		<link>https://www.bram.us/2026/04/02/view-transitions-toolkit/</link>
					<comments>https://www.bram.us/2026/04/02/view-transitions-toolkit/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Thu, 02 Apr 2026 20:03:48 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[view transitions]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36216</guid>

					<description><![CDATA[<p>In my work with View Transitions over the last several years, I’ve published everything from deep-dive articles, demos, and announcement videos at Google I/O. I’ve also done some more experimental things with it, such as <a href="https://www.bram.us/2025/02/07/view-transitions-applied-more-performant-view-transition-group-animations/">optimizing the keyframes</a> or <a href="https://www.bram.us/2024/04/29/if-view-transitions-and-scroll-driven-animations-had-a-baby-css-cafe/">driving a View Transition by scroll</a>.</p>

<p>To turn the lessons from these scattered experiments into something more reusable for both you and me, I’ve bundled the most frequent code patterns into a dedicated package: <a href="https://chrome.dev/view-transitions-toolkit/"><code>view-transitions-toolkit</code></a>.</p>]]></description>
										<content:encoded><![CDATA[<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/04/view-transitions-toolkit-main.png"><img fetchpriority="high" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/04/view-transitions-toolkit-main.png" alt="" width="560" height="370" class="alignnone size-medium wp-image-36217" /></a><figcaption>Screenshot of <a href="https://chrome.dev/view-transitions-toolkit/">the View Transitions Toolkit homepage</a></figcaption></figure>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="intro">
<p>In my work with View Transitions over the last several years, I’ve published everything from deep-dive articles, demos, and announcement videos at Google I/O. I’ve also done some more experimental things with it, such as <a href="https://www.bram.us/2025/02/07/view-transitions-applied-more-performant-view-transition-group-animations/">optimizing the keyframes</a> or <a href="https://www.bram.us/2024/04/29/if-view-transitions-and-scroll-driven-animations-had-a-baby-css-cafe/">driving a View Transition by scroll</a>.</p>
<p>To turn the lessons from these scattered experiments into something more reusable for both you and me, I’ve bundled the most frequent code patterns into a dedicated package: <a href="https://chrome.dev/view-transitions-toolkit/"><code>view-transitions-toolkit</code></a>.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The <code>view-transitions-toolkit</code></h3>
<p><code>view-transitions-toolkit</code> is a collection of small, focused helper functions for use with View Transitions. They fill in some of the gaps to making advanced patterns much easier to implement.</p>
<ul>
<li><strong>Feature Detection</strong>: Get information about whether certain View Transitions sub-features are supported.</li>
<li><strong>Shim Support</strong>: Shim support for <code>document.activeViewTransition</code>.</li>
<li><strong>Animations</strong>: Utilities for extracting, measuring, and optimizing animations.</li>
<li><strong>Transition Playback Control</strong>: Pause, Resume, or Scrub the playback of a View Transition.</li>
<li><strong>Automatic Page Navigation Types</strong>: Automatically inject View Transition Types based on navigation origin/destination.</li>
</ul>
<p>The goal of the project is to provide these utilities so you don&#8217;t have to reinvent the wheel every time you want to do something slightly advanced with View Transitions.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Getting Started</h3>
<p>Using the toolkit is incredibly simple. First, install it from npm:</p>
<pre><code>npm i view-transitions-toolkit</code></pre>
<p>Then, you can import the modules you need and use their functions. Optimizing the keyframes of a View Transition group for example is a simple one-liner:</p>
<pre><code class="language-js">import { optimizeGroupAnimations, OPTIMIZATION_STRATEGY } from "view-transitions-toolkit/animations";

const t = document.startViewTransition(() => { … });
await t.ready;

// Optimize all Group Animations using the default SCALE strategy
optimizeGroupAnimations(t, "*");

// Optimize only the `::view-transition-group(box-flip)` animation using the SLIDE strategy
optimizeGroupAnimations(t, "box-flip", OPTIMIZATION_STRATEGY.SLIDE);</code></pre>
<p>I won’t go into details here, as all features are documented in <a href="https://github.com/GoogleChromeLabs/view-transitions-toolkit/tree/main/docs">the docs folder</a> of the repo.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Demos</h3>
<p>The project also comes with a public website that not only gives you the basic info, but also comes with a bunch of demos: <a href="https://chrome.dev/view-transitions-toolkit/">https://chrome.dev/view-transitions-toolkit/</a>.</p>
<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/04/view-transitions-toolkit-optimize.png"><img decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/04/view-transitions-toolkit-optimize.png" alt="" width="560" height="370" class="alignnone size-medium wp-image-36218" /></a><figcaption>Screenshot of <a href="https://chrome.dev/view-transitions-toolkit/optimize/">the <code>optimize()</code> demo</a></figcaption></figure>
<p>The source of the demos is included in the repository as well.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Go check it out</h3>
<ul>
<li><a href="https://chrome.dev/view-transitions-toolkit/"><code>view-transitions-toolkit</code> demos  (Chrome.dev) &rarr;</a></li>
<li><a href="https://github.com/googlechromelabs/view-transitions-toolkit/"><code>view-transitions-toolkit</code> source (GitHub) &rarr;</a></li>
<li><a href="https://www.npmjs.com/package/view-transitions-toolkit"><code>view-transitions-toolkit</code> on NPM &rarr;</a></li>
</ul>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Spread the word</h3>
<p>Feel free to reshare one of the following posts on social media to help spread the word:</p>
<ul>
<li><a href="https://bsky.app/profile/bram.us/post/3mijlrthmc22v">🦋 Bluesky</a></li>
<li><a href="https://front-end.social/@bramus/116335982870732034">🦣 Mastodon</a></li>
<li><a href="https://www.linkedin.com/feed/update/urn:li:activity:7445503375812567041/">💼 LinkedIn</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/04/02/view-transitions-toolkit/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>CSS position: sticky now sticks to the nearest scroller on a per axis basis!</title>
		<link>https://www.bram.us/2026/03/30/css-sticky-per-axis/</link>
					<comments>https://www.bram.us/2026/03/30/css-sticky-per-axis/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Mon, 30 Mar 2026 12:21:18 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[sticky]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36196</guid>

					<description><![CDATA[<p>If you’ve ever tried to build a data table with a sticky header and a sticky first column, you know the pain. You’d think a simple <code>position: sticky</code> with <code>top: 0</code> and <code>left: 0</code> would be enough, but the reality was that only one of both would stick.</p>

<p>A recent change to CSS fixes this: <code>position: sticky</code> now plays nice with <em>single-axis scrollers</em>, allowing you to have sticky elements that track different scroll containers on different axes. This change is available in Chrome 148.</p>]]></description>
										<content:encoded><![CDATA[<!-- Added by Post/Page Specific Custom Code plugin, thank you for using! -->
<style>/* DL GRID @src https://codepen.io/bramus/pen/POEaXg*/
@media(min-width: 30em) {
dl {
	display: grid;
	grid-template: auto / 10em 1fr;
}

dt {
  grid-column: 1;
}

dd {
  grid-column: 2;
}

dt, dd {
	margin: 0;
	padding: .3em .5em;
	border-top: 1px solid rgba(0,0,0,.1);
}

:is(dt, dd) > :first-child {
  margin-top: 0;
}
:is(dt, dd) > :last-child {
  margin-bottom: 0;
}
}</style>
<figure><div style="width: 640px;" class="wp-video"><video class="wp-video-shortcode" id="video-36196-1" width="640" height="391" loop autoplay muted preload="metadata" controls="controls"><source type="video/mp4" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/sticky-per-axis-2.mp4?_=1" /><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/03/sticky-per-axis-2.mp4">https://www.bram.us/wordpress/wp-content/uploads/2026/03/sticky-per-axis-2.mp4</a></video></div><figcaption>Recording of <a href="https://codepen.io/bramus/pen/VYKQwmK">the demo</a>.</figcaption></figure>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="intro">
<p>If you’ve ever tried to build a data table with a sticky header and a sticky first column, you know the pain. You’d think a simple <code>position: sticky</code> with <code>top: 0</code> and <code>left: 0</code> would be enough, but the reality was that only one of both would stick.</p>
<p>A recent change to CSS fixes this: <code>position: sticky</code> now plays nice with <em>single-axis scrollers</em>, allowing you to have sticky elements that track different scroll containers on different axes. This change is available in Chrome 148.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#the-situation" name="the-situation">#</a> The Situation</h3>
<p>To understand the impact, let&#8217;s quickly review the standard setup for a responsive table. You start with a wide HTML <code>&lt;table&gt;</code> full of data. To prevent this wide table from breaking your page layout on small screen devices, you typically wrap it in a container with <code>overflow-x: auto</code>.</p>
<pre><code class="language-html">&lt;div class="table-wrapper" style="overflow-x: auto;"&gt;
  &lt;table&gt;
    …
  &lt;/table&gt;
&lt;/div&gt;
</code></pre>
<p>Because you want the column headers (the <code>&lt;thead&gt;</code>) to stay visible when scrolling down the document, you apply <code>position: sticky; top: 0;</code> to them. Simultaneously, you want your first column to stay visible when scrolling sideways through the data, so you apply <code>position: sticky; left: 0;</code> there.</p>
<pre><code class="language-css">.table-wrapper {
  overflow-x: auto;
}

.table-wrapper thead {
  position: sticky;
  top: 0;
}

.table-wrapper td:first-child {
  position: sticky;
  left: 0;
}
</code></pre>
<p>However, this doesn&#8217;t work as expected. While the first column will stick to the left edge of the table wrapper when scrolling horizontally, the headers will scroll out of view along with the rest of the page.</p>
<p>This is because the <code>.table-wrapper</code> becomes the sticky reference for <em>both</em> axes. Because the wrapper only scrolls horizontally, the vertical sticking becomes completely ineffective. Your <code>top: 0</code> headers would just happily scroll out of view along with the rest of the page 😭</p>
<p>To get around this, you had to rely on lots of JavaScript to synchronize the scroll position, or rely on duplicated headers.</p>
<div class="note">
<p>Note: While there are <a href="https://css-tricks.com/a-table-with-both-a-sticky-header-and-a-sticky-first-column/">pure CSS solutions</a> to make sure the headers also stick within the <code>.table-wrapper</code>, the problem described here is different: instead of getting the headers to stick inside the <strong>same</strong> scroller, you want the headers to be stuck against a <strong>different</strong> scroller <em>(here: the <code>.table-wrapper</code> for horizontally sticky stuff, and the document’s scroller for vertically sticky stuff)</em>.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#the-change" name="the-change">#</a> The Change</h3>
<p>A recent change to CSS fixes this issue: <code>position: sticky</code> can now track the nearest scrolling container <em>per axis</em>.</p>
<p>This means that for our wide data table:</p>
<ul>
<li>The first column can be sticky on the horizontal axis against the table wrapper.</li>
<li>The headers can be sticky on the vertical axis against a completely different scroller — such as the document itself!</li>
</ul>
<p>No more duplicating headers. No more scroll-syncing JavaScript. Just plain CSS doing exactly what you&#8217;d expect it to do.</p>
<div class="note">
<p>The request for this change was filed back in 2017, in CSS WG Issue <a href="https://github.com/w3c/csswg-drafts/issues/865">#865</a>. It’s not that often you see three-digit issues get resolved and implemented 😅</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#see-it-in-action" name="see-it-in-action">#</a> See it in action</h3>
<p>You can see this new behavior in action in this CodePen demo:</p>
<p class="codepen" data-height="760" data-pen-title="CSS `position: sticky` for Single Axis Scroll Containers" data-default-tab="result" data-slug-hash="VYKQwmK" data-user="bramus" style="height: 760px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/bramus/pen/VYKQwmK"><br />
  CSS `position: sticky` for Single Axis Scroll Containers</a> by Bramus (<a href="https://codepen.io/bramus">@bramus</a>)<br />
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<p><script async src="https://public.codepenassets.com/embed/index.js"></script></p>
<p>If you view the demo in a supported browser, the first column will stick to the left edge of the table wrapper when scrolling horizontally, and the top headers will stick to the top of the viewport when scrolling vertically.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#an-important-detail-watch-your-overflow" name="an-important-detail-watch-your-overflow">#</a> An Important Detail: Watch your overflow</h3>
<p>If you look closely at the CSS in the demo, you might notice a very specific detail in how the <code>.table-wrapper</code> is styled.</p>
<pre><code class="language-css">.table-wrapper {
  overflow: auto clip;
}</code></pre>
<p>To make the wrapper scroll horizontally, you might instinctively reach for <code>overflow-x: scroll</code> (or <code>auto</code>). However, doing so will actually break the vertical stickiness we just achieved!</p>
<p>Why? Because in CSS, if you set <code>overflow-x</code> to <code>scroll</code> or <code>auto</code>, the browser automatically computes <code>overflow-y</code> to <code>auto</code> as well (assuming its value was <code>visible</code>). This turns the wrapper into a scroll container on the vertical axis too, which means it once again traps our headers and becomes their sticky reference.</p>
<p>To fix this, we need to explicitly tell the browser <em>not</em> to create a scroll container on the block axis. We do this by using the <code>clip</code> keyword:</p>
<pre><code class="language-css">.table-wrapper {
  /* ❌ This computes overflow-y to auto, breaking vertical stickiness */
  /* overflow-x: auto; */

  /* ✅ This creates a scroller on the inline axis, but clips the block axis */
  overflow: auto clip; 
}</code></pre>
<p>By setting <code>overflow-y</code> to <code>clip</code> (via the <code>overflow</code> shorthand), the wrapper does not become a scrollport on the vertical axis. The headers are therefore free to track the document viewport instead.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#browser-support" name="browser-support">#</a> Browser Support</h3>
<div class="note note--supergreen">
<p>💡 Although this post was originally published in March 2026, the section below is constantly being updated. <em>Last update: March 30, 2026</em>.</p>
</div>
<p>At the time of writing, this feature is only supported in Chrome 148</p>
<dl>
<dt>Chromium <em>(Blink)</em></dt>
<dd>
<p>✅ Available in Chromium 148.0.7742.0.</p>
</dd>
<dt>Firefox <em>(Gecko)</em></dt>
<dd>
<p>❌ No support</p>
<p>Subscribe to <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=2023702">Bug #2023702</a> for updates.</p>
</dd>
<dt>Safari <em>(WebKit)</em></dt>
<dd>
<p>❌ No support</p>
</dd>
</dl>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#feature-detection" name="feature-detection">#</a> Feature Detection</h3>
<p>Because older browsers still use the inner container as the sticky reference for both axes, you might want to feature-detect this new behavior to provide JS-based fallbacks or conditionally load specific styles.</p>
<p>While we can&#8217;t easily detect this layout behavior directly with an <code>@supports</code> query in CSS yet — I have an <a href="https://github.com/w3c/csswg-drafts/issues/13677">open issue at the CSSWG for this</a> — you can feature detect this using JavaScript. The <a href="https://codepen.io/bramus/full/VYKQwmK">CodePen demo linked above</a> includes a script that does exactly this.</p>
<p>It handles the detection by evaluating how the browser resolves the sticky positioning offsets within a scrolling wrapper. Feel free to peek at the JS tab in the Pen to grab the snippet for your own projects.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#spread-the-word" name="spread-the-word">#</a> Spread the word</h3>
<p>Feel free to reshare one of the following posts on social media to help spread the word:</p>
<ul>
<li><a href="https://bsky.app/profile/bram.us/post/3mibo3clru22q">🦋 Bluesky</a></li>
<li><a href="https://front-end.social/@bramus/116318130490039355">🦣 Mastodon</a></li>
<p>  <!-- 

<li>💼 LinkedIn</li>

 -->
</ul>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/03/30/css-sticky-per-axis/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://www.bram.us/wordpress/wp-content/uploads/2026/03/sticky-per-axis-2.mp4" length="1589051" type="video/mp4" />

			</item>
		<item>
		<title>More Easy Light-Dark Mode Switching: light-dark() is about to support images!</title>
		<link>https://www.bram.us/2026/03/19/more-easy-light-dark-mode-switching-light-dark-is-about-to-support-images/</link>
					<comments>https://www.bram.us/2026/03/19/more-easy-light-dark-mode-switching-light-dark-is-about-to-support-images/#comments</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Thu, 19 Mar 2026 11:24:41 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[dark mode]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36160</guid>

					<description><![CDATA[<p>CSS <strong><code>light-dark()</code> is being extended to support images.</strong></p>]]></description>
										<content:encoded><![CDATA[<!-- Added by Post/Page Specific Custom Code plugin, thank you for using! -->
<style>/* DL GRID @src https://codepen.io/bramus/pen/POEaXg*/
@media(min-width: 30em) {
dl {
	display: grid;
	grid-template: auto / 10em 1fr;
}

dt {
  grid-column: 1;
}

dd {
  grid-column: 2;
}

dt, dd {
	margin: 0;
	padding: .3em .5em;
	border-top: 1px solid rgba(0,0,0,.1);
}

:is(dt, dd) > :first-child {
  margin-top: 0;
}
:is(dt, dd) > :last-child {
  margin-bottom: 0;
}
}</style>
<p><img decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/light-dark-image.png" alt="" width="560" height="264" class="alignnone size-medium wp-image-36172" /></p>
<div class=intro>
<p>Back in 2023, I wrote about <a href="https://www.bram.us/2023/10/09/the-future-of-css-easy-light-dark-mode-color-switching-with-light-dark/">the future of CSS color switching</a> using the then-novel <code>light-dark()</code> function. It was a game-changer for colors, allowing us to ditch the repetitive <code>@media (prefers-color-scheme: ...)</code> blocks for simple property declarations.</p>
<p>But there was one glaring limitation: it only works for colors. If you wanted to swap out a background image, a mask, or a logo based on the user&#8217;s color scheme, you were stuck doing things the &#8220;old&#8221; way.</p>
<p>Well, I have good news. The spec has been updated, and <strong><code>light-dark()</code> is being extended to support images.</strong></p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#missing-piece" name="missing-piece">#</a> The Missing Piece</h3>
<p>As a recap, in CSS, you have to write something like this if you want to set background-images for light and dark mode:</p>
<pre><code class="lang-css" style="tab-size: 2">:root {
  --bg-image: url(light-pattern.png);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-image: url(dark-pattern.png);
  }
}

.element {
  background-image: var(--bg-image);
}</code></pre>
<p>This code has downsides: The necessary parts can be scattered all over the place and it only checks the global color-scheme preference without respecting local color-scheme overrides done with <a href="https://developer.mozilla.org/docs/Web/CSS/Reference/Properties/color-scheme">the <code>color-scheme</code> CSS property</a>.</p>
<p>Thanks CSS <a href="https://drafts.csswg.org/css-color-5/#light-dark"><code>light-dark()</code></a> we can keep our code closely together, and can respond to the local used <code>color-scheme</code> value. In it’s updated form – in which <code>light-dark()</code> now also supports <code>&lt;image&gt;</code> values – that whole block can be collapsed into a single rule, and we can make it respect local <code>color-scheme</code> overrides:</p>
<pre><code class="lang-css" style="tab-size: 2">.element {
  color-scheme: dark;
  background-image: light-dark(url(light-pattern.png), url(dark-pattern.png));
}</code></pre>
<p>Sweet!</p>
<div class=note>
<p>Note that you must pass in either two <code>&lt;color&gt;</code> values or two <code>&lt;image&gt;</code> values as arguments to <code>light-dark()</code> … you can’t mix the types — see <a href="#other-values">further down</a> for an explanation why that is.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#browser-support" name="browser-support">#</a> Browser Support</h3>
<div class="note note--supergreen">
<p>💡 Although this post was originally published in March 2026, the section below is constantly being updated. <em>Last update: March 20, 2026</em>.</p>
</div>
<p>It’s early days, but the engines are already moving:</p>
<dl>
<dt>Chromium <em>(Blink)</em></dt>
<dd>
<p>👨‍🔬 Available in Chromium 148.0.7742.0 or newer behind the <strong>Experimental Web Platform Features</strong> flag (<code>chrome://flags/#enable-experimental-web-platform-features</code>).</p>
<p>No word yet on which stable version will ship it by default. The <a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/We0pqxS4IcQ/m/yZ6AwYW8BgAJ">paperwork</a> is in progress. Subscribe to <a href="https://issues.chromium.org/issues/491829958">CrBug #491829958</a> for updates.</p>
</dd>
<dt>Firefox <em>(Gecko)</em></dt>
<dd>
<p>✅ Supported in Firefox 150</p>
<p>Firefox 150 is expected to go stable on April 21, 2026</p>
</dd>
<dt>Safari <em>(WebKit)</em></dt>
<dd>
<p>❌ No support</p>
<p>Subscribe to <a href="https://bugs.webkit.org/show_bug.cgi?id=309689">WebKit Bug #309689</a> for updates.</p>
</dd>
</dl>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#feature-detection" name="feature-detection">#</a> Feature Detection</h3>
<p>If you want to start experimenting today while providing a fallback, you can use <code>@supports</code>. To detect support for images specifically, you can test it using <code>linear-gradient()</code> (which is treated as an <code>&lt;image&gt;</code> in CSS):</p>
<pre><code class="lang-css" style="tab-size: 2">@supports (background-image: light-dark(linear-gradient(white, white), linear-gradient(black, black))) {
  /* Modern image-switching logic here */
}</code></pre>
<p>You can see the code in action this CodePen:</p>
<p class="codepen" data-height="590" data-pen-title="CSS light-dark(&amp;lt;image&amp;gt;, &amp;lt;image&amp;gt;) Support test" data-default-tab="result" data-slug-hash="bNwRmgm" data-user="bramus" style="height: 590px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;"><span>See the Pen <a href="https://codepen.io/bramus/pen/bNwRmgm">CSS light-dark(&lt;image&gt;, &lt;image&gt;) Support test</a> by Bramus (<a href="https://codepen.io/bramus">@bramus</a>) on <a href="https://codepen.io">CodePen</a>.</span></p>
<p><script async src="https://public.codepenassets.com/embed/index.js"></script></p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#other-values" name="other-values">#</a> What about non-<code>&lt;color&gt;</code> and non-<code>&lt;image&gt;</code> values?</h3>
<p>Supporting non-<code>&lt;color&gt;</code> and non-<code>&lt;image&gt;</code> values still is an unanswered question (that probably won’t get solved). As explained in my original coverage, CSS can’t just simply accept <code>light-dark()</code> anywhere because <a href="https://www.w3.org/TR/css-syntax-3/#parse-grammar">the parser needs to know the value type of what it is parsing</a> ahead of time.</p>
<p>Internally, <code>light-dark()</code> is <a href="https://github.com/w3c/csswg-drafts/issues/12513#issuecomment-4085662455">about to be defined</a> as having two internal variants – one that accepts <code>&lt;color&gt;</code>s and one that accepts <code>&lt;image&gt;</code>s:</p>
<pre><code>light-dark() =  &#x3C;light-dark-color&#x3E; | &#x3C;light-dark-image&#x3E;
&#x3C;light-dark-color&#x3E; = light-dark(&#x3C;color&#x3E;, &#x3C;color&#x3E;)
&#x3C;light-dark-image&#x3E; = light-dark(&#x3C;image&#x3E;, &#x3C;image&#x3E;)</code></pre>
<p>Each variant has limitations on where it can be used: the version that does colors is only accepted where <code>&lt;color&gt;</code>s are allowed, and the version that does images is only accepted where <code>&lt;image&gt;</code>s are allowed.</p>
<p><pre><code>&#x3C;color&#x3E; = &#x3C;color-base&#x3E; | currentColor | &#x3C;system-color&#x3E; | 
          &#x3C;contrast-color()&#x3E; | &#x3C;device-cmyk()&#x3E; | <b>&#x3C;light-dark-color&#x3E;</b></code></pre>
<pre><code>&#x3C;image&#x3E; = &#x3C;url&#x3E; | &#x3C;image()&#x3E; | &#x3C;image-set()&#x3E; | &#x3C;cross-fade()&#x3E; | 
          &#x3C;element()&#x3E; | &#x3C;gradient&#x3E; | <b>&#x3C;light-dark-image&#x3E;</b></code></pre>
<p>If more types of values needed to be supported, that would required many more internal variants.</p>
<p>This split into two variants allows the CSS parser to discard invalid declarations at <em>parse time</em>. That, in turn, explains why you can’t mix the types in the arguments. Say CSS were to accept a mix of <code>&lt;color&gt;</code> and <code>&lt;image&gt;</code>, then you would be able declare something like <code>background: red light-dark(blue, url(dark.png));</code>. With a dark <code>color-scheme</code> that declaration would end up being OK, but in a light <code>color-scheme</code> you’d end up with <code>background: red blue</code> which is invalid.</p>
<h4>Looking ahead: <code>@function</code> + <code>color-scheme()</code> to the rescue!</h4>
<p>In the future there will be an way to have non-<code>&lt;color&gt;</code> and non-<code>&lt;image&gt;</code> <code>color-scheme</code>-dependent values in the future: using a CSS Custom Function and <code>color-scheme()</code>. Go check out my previous post on <a href="https://www.bram.us/2025/09/30/css-custom-light-dark/">implementing a custom <code>--light-dark()</code> function that works with <em>any</em> type of value</a> for the details. It’s just 3 lines of code, which I’ve included here as well:</p>
<pre><code class="language-css" style="tab-size: 2">@function --light-dark(--l, --d) {
  result: if(color-scheme(dark): var(--d); else: var(--l));
}</code></pre>
<p>To feature detect support for <code>@function</code>, <a href="https://www.bram.us/2026/03/15/at-rule/">check out <code>@supports at-rule()</code></a>. There currently is no easy way to detect support for <code>color-scheme()</code>, unless <a href="https://codepen.io/bramus/pen/bNEWZLV/2f99187f31ee53f87a1b48bb083cb41e">you want to jump through some hoops</a>.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#spread-the-word" name="spread-the-word">#</a> Spread the word</h3>
<p>Feel free to reshare one of the following posts on social media to help spread the word:</p>
<ul>
<li><a href="https://bsky.app/profile/bram.us/post/3mhfy336xs22w">🦋 BlueSky</a></li>
<li><a href="https://front-end.social/@bramus/116255783077151930">🦣 Mastodon</a></li>
<li><a href="https://www.linkedin.com/feed/update/urn:li:activity:7440377308567322625/">💼 LinkedIn</a></li>
</ul>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/03/19/more-easy-light-dark-mode-switching-light-dark-is-about-to-support-images/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Detect at-rule support in CSS with @supports at-rule(@keyword)</title>
		<link>https://www.bram.us/2026/03/15/at-rule/</link>
					<comments>https://www.bram.us/2026/03/15/at-rule/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Sun, 15 Mar 2026 19:51:22 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[feature detection]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36134</guid>

					<description><![CDATA[<p>Back in January 2022, I <a href="https://brm.us/at-rule-2022">wrote</a> about an exciting new CSS Working Group decision: a function to detect at-rule support using <code>@supports at-rule(@keyword)</code>. Fast forward to today, and the CSS Conditional Rules Module Level 5 specification has solidified how this feature works and Chromium (Chrome, Edge, etc.) is about to ship it in Chromium 148!</p>]]></description>
										<content:encoded><![CDATA[<p><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule.png" alt="" width="560" height="336" class="alignnone size-medium wp-image-36142" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule.png 2333w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-560x336.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-1120x672.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-768x461.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-1536x922.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-2048x1229.png 2048w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/at-rule-1568x941.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /></p>
<div class="intro">
<p>Back in January 2022, I <a href="https://brm.us/at-rule-2022">wrote</a> about an exciting new CSS Working Group decision: a function to detect at-rule support using <code>@supports at-rule(@keyword)</code>. Fast forward to today, and the CSS Conditional Rules Module Level 5 specification has solidified how this feature works and Chromium (Chrome, Edge, etc.) is about to ship it in Chromium 148!</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The solution (Updated)</h3>
<p>The core idea remains the same as back in 2022: use the <code>at-rule()</code> function within an <code>@supports</code> query to check if a user agent recognizes a specific at-rule.</p>
<pre><code class="language-css">@supports at-rule(@starting-style) {
  /* CSS for browsers that support @starting-style here … */
}</code></pre>
<p>The function simply checks if the browser would accept an at-rule in any context. This is super useful for detecting entire new features like <code>@starting-style</code>.</p>
<p>You can also use <code>at-rule()</code> in the <em>supports-condition</em> when doing an <code>@import</code>:</p>
<pre><code class="language-css">/* This CSS only gets imported when @view-transition is supported (and the browser supports at-rule() as well) */
@import "view-transitions.css" supports(at-rule(@view-transition));</code></pre>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>No descriptors, preludes, or full blocks!</h3>
<p>In my <a href="https://brm.us/at-rule-2022">original coverage of <code>at-rule()</code></a>, I mentioned an extension to the feature that would theoretically allow you to detect support for specific descriptors (e.g. <code>at-rule(@counter-style; system: fixed)</code>) or even pass in a full at-rule block.</p>
<p>This extension — along with checking for at-rule <em>preludes</em> (= the part between the at-rule and the opening <code>{</code>) — <a href="https://github.com/w3c/csswg-drafts/issues/6966#issuecomment-3205037703">has been dropped</a>. The following examples will all fail, as those are not supported:</p>
<pre><code class="language-css">/* ❌ Passing in descriptors is not supported */
@supports at-rule(@counter-style; system: fixed) { … }

/* ❌ Passing in full at-rules is not supported */
@supports at-rule(@counter-style { system: fixed }) { … }

/* ❌ Passing in preludes of at-rules is not supported */
@supports at-rule(@container style(…)) { … }</code></pre>
<p>Because preludes are not allowed in <code>at-rule()</code>, it cannot be used to detect non-size <code>@container</code> queries support <em>(e.g. style queries, anchored queries, etc.)</em>. To detect those, fall back to sniffing out support for the various values of <code>container-type</code> — a technique you can already use today in all browsers:</p>
<pre><code class="language-css">@supports (container-type: anchored) {
  /* CSS for browsers that support anchored queries here … */
}</code></pre>
<div class="note">
<p>Note that you also can’t pass <code>@charset</code> into <code>at-rule()</code>, as it technically is <a href="https://drafts.csswg.org/css-syntax/#charset-rule">not an at-rule</a>.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Browser Support</h3>
<p><code>@supports at-rule(@keyword)</code> is available in Chromium 148. The initial implementation was done <a href="https://chromium-review.googlesource.com/5972643">by Google</a> and Kevin from Microsoft <a href="https://chromium-review.googlesource.com/7037634">pushed it over the finish line</a> while also navigation the paperwork with the CSS Working Group.</p>
<dl>
<dt>Chromium <em>(Blink)</em></dt>
<dd>
<p>✅ Supported in Chromium 148 and up.</p>
</dd>
<dt>Firefox <em>(Gecko)</em></dt>
<dd>
<p>❌ No support</p>
<p>Follow <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1751188">Issue #1751188</a> to stay up-to-date and signal interest.</p>
</dd>
<dt>Safari <em>(WebKit)</em></dt>
<dd>
<p>❌ No support</p>
<p>Subscribe to <a href="https://bugs.webkit.org/show_bug.cgi?id=235400">Issue #235400</a> to stay up-to-date and signal interest.</p>
</dd>
</dl>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Feature Detection</h3>
<p>You can feature detect support for <code>at-rule()</code> by checking for support for <code>@supports</code> itself.</p>
<pre><code class="language-css">@supports at-rule(@supports) {
  /* ✅ The browser supports `@supports at-rule(…)` */
}</code></pre>
<p>You can see it in action in the following demo:</p>
<p class="codepen" data-height="600" data-pen-title="CSS @supports at-rule() test" data-default-tab="result" data-slug-hash="LERWgbe" data-user="bramus" style="height: 600px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/bramus/pen/LERWgbe"><br />
  CSS @supports at-rule() test</a> by Bramus (<a href="https://codepen.io/bramus">@bramus</a>)<br />
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<p><script async src="https://public.codepenassets.com/embed/index.js"></script></p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/03/15/at-rule/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Introducing view-transitions-mock: A non-visual Polyfill for Same-Document View Transitions</title>
		<link>https://www.bram.us/2026/03/11/view-transitions-mock-is-a-non-visual-polyfill-for-same-document-view-transitions/</link>
					<comments>https://www.bram.us/2026/03/11/view-transitions-mock-is-a-non-visual-polyfill-for-same-document-view-transitions/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Tue, 10 Mar 2026 23:33:07 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[polyfill]]></category>
		<category><![CDATA[view transitions]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36116</guid>

					<description><![CDATA[<p>View Transitions are a powerful Modern Web feature allow for smooth seamless animated transitions two between different states of a web page. They can make for a much more pleasant user experience, but as with any new web platform feature, browser support is not yet fully universal so you need to add some fallback logic to your code.</p><p><a href="https://github.com/GoogleChromeLabs/view-transitions-mock"><code>view-transitions-mock</code></a> bridges that gap. It is a spec-compliant JavaScript implementation of Same-Document View Transitions that polyfills the entire JavaScript API surface of the spec, but that doesn’t do the animation bits.</p>]]></description>
										<content:encoded><![CDATA[<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock.png"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock.png" alt="" width="560" height="315" class="alignnone size-medium wp-image-36117" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock.png 1920w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-560x315.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-1120x630.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-768x432.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-1536x864.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-1568x882.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /></a><figcaption>Schematic of transitioning from State A to State B. The top row shows the steps of a native Same-Document View Transitions implementation. The bottom row shows how <code>view-transitions-mock</code> handles it.</figcaption></figure>
<div class=intro>
<p>View Transitions are a powerful Modern Web feature allow for smooth seamless animated transitions two between different states of a web page. They can make for a much more pleasant user experience, but as with any new web platform feature, browser support is not yet fully universal so you need to add some fallback logic to your code.</p>
<p><a href="https://github.com/GoogleChromeLabs/view-transitions-mock"><code>view-transitions-mock</code></a> bridges that gap. It is a spec-compliant JavaScript implementation of Same-Document View Transitions that polyfills the entire JavaScript API surface of the spec, but that doesn’t do the animation bits.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The Challenge: Writing Future-Proof Code</h3>
<p>When using new APIs like the View Transition API, you need to write defensive code that check for browser support:</p>
<pre><code class="language-js">if (!document.startViewTransition) {
  updateTheDOM();
} else {
  document.startViewTransition(updateTheDOM);
}</code></pre>
<p>This clutters your codebase with <code>if (document.startViewTransition)</code> guards which makes it harder to maintain. And that list of safeguards quickly grows, for example when you throw View Transition Types in the mix.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The Solution: <code>view-transitions-mock</code></h3>
<p>Ideally, you want to write code that will work seamlessly in all browsers, regardless of whether they support Same-Document View Transitions natively or not. The bridge that gap I created <a href="https://github.com/GoogleChromeLabs/view-transitions-mock"><code>view-transitions-mock</code>, a non-visual polyfill that mocks the entire JavaScript API surface of Same-Document View Transitions</a>, including:</p>
<ul>
<li><code>document.startViewTransition()</code></li>
<li>The <code>ViewTransition</code> class, including:
<ul>
<li>The <code>updateCallbackDone</code>, <code>ready</code>, and <code>finished</code> promises.</li>
<li>The <code>ViewTransition.transitionRoot</code> property.</li>
</ul>
</li>
<li><code>document.activeViewTransition</code></li>
<li>View Transition Types</li>
</ul>
<p>What it <strong>doesn&#8217;t</strong> polyfill are the visual aspects: the pseudo-tree, the CSS properties, and the animations themselves. This means that while you won&#8217;t see any animations in browsers without native support, yet your code will still execute without errors, and the DOM will be updated as expected.</p>
<div class="note note--info">
<p>ℹ️ <code>view-transitions-mock</code> is different from <a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#not-a-polyfill">earlier attempts to solve this problem</a>.</p>
<p>Where earlier attempts just give you back an object with pre-resolved promises, <b><code>view-transitions-mock</code> is very different because it is an actual spec-compliant JavaScript implementation of <a href="https://drafts.csswg.org/css-view-transitions-1/">the css-view-transitions-1 specification</a> (with some additions from <a href="https://drafts.csswg.org/css-view-transitions-2/">css-view-transitions-2</a>, such as View Transition Types).</b></p>
<p>This means that the handling and manipulation of the <code>ViewTransition</code> promises or the View Transition Types behave exactly the same as a native implementation in browsers.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Getting Started</h3>
<p>Using <code>view-transitions-mock</code> is incredibly simple. First, install it from npm:</p>
<pre><code>npm i view-transitions-mock</code></pre>
<p>Then, import and register it in your code (before calling <code>document.startViewTransition</code>):</p>
<pre><code class="language-js">import { register } from "view-transitions-mock";
register();</code></pre>
<p>That&#8217;s it! You can now use the View Transition API without any worries.</p>
<pre><code class="language-js">import { register } from "view-transitions-mock";
register();

document.startViewTransition(() => {
  // …
});</code></pre>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Tweaking the registration</h3>
<p>By default, the registration of <code>view-transitions-mock</code> checks whether <code>document.startViewTransition</code> and View Transition Types are supported or not. When both are natively supported, it won’t register the mock.</p>
<p>You can tweak the registration by passing an object with the following properties into the <code>register</code> function:</p>
<ul>
<li><code>requireTypes</code> <em>(Boolean, default value: <code>true</code>)</em>: Require support for View Transition Types</li>
<li><code>forced</code> <em>(Boolean, default value: false)</em>: Force register the mock, regardless of support.</li>
</ul>
<p>For example, if you are not relying on View Transition Types, call <code>register</code> as follows so that it does not register the mock in Firefox 144–146 (which does not have support for View Transition Types):</p>
<pre><code class="language-js">import { register } from "view-transitions-mock";
register({ requireTypes: false });</code></pre>
<p>Or if you want to disable native support for Same-Document View Transitions entirely – handy if you want to test how your site looks without View Transitions – call <code>register</code> as follows:</p>
<pre><code class="language-js">import { register } from "view-transitions-mock";
register({ forced: true });</code></pre>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>See it in Action</h3>
<p>Over at <a href="https://chrome.dev/view-transitions-mock">https://chrome.dev/view-transitions-mock</a> you can find a live demo of <code>view-transitions-mock</code>. Every time you click the area around the box a View Transition gets started and output is added to the log.</p>
<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-demo.png"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/view-transitions-mock-demo.png" alt="" width="560" height="364" class="alignnone size-medium wp-image-36124" /></a><figcaption>Screenshot of <a href="https://chrome.dev/view-transitions-mock">https://chrome.dev/view-transitions-mock</a> in Chrome</figcaption></figure>
<p>By default won’t see much happening in the demo, because the thing that you should be seeing is that it doesn’t crash in browsers with no support for Same-Document View Transitions. You can use one of the buttons on the page to do a forced registration of <code>view-transitions-mock</code>. After having done so, you’ll see stuff get added to the log, but no visual transition happening. This is the <code>view-transitions-mock</code> in action!</p>
<p>The source code for the demo is also available in <a href="https://github.com/GoogleChromeLabs/view-transitions-mock">the repository</a>.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<p>To learn more about <code>view-transitions-mock</code>, check out the following links if you haven’t done so already:</p>
<ul>
<li><a href="https://github.com/GoogleChromeLabs/view-transitions-mock"><code>view-transitions-mock</code> Source (GitHub) &rarr;</a></li>
<li><a href="https://chrome.dev/view-transitions-mock"><code>view-transitions-mock</code> Demo &rarr;</a></li>
<li><a href="https://www.npmjs.com/package/view-transitions-mock"><code>view-transitions-mock</code> on NPMJS &rarr;</a></li>
<li><a href="https://npmx.dev/package/view-transitions-mock"><code>view-transitions-mock</code> on npmx &rarr;</a></li>
</ul>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/03/11/view-transitions-mock-is-a-non-visual-polyfill-for-same-document-view-transitions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Experiment: Animating CSS position-area with View Transitions</title>
		<link>https://www.bram.us/2026/03/02/animating-css-position-area-with-view-transitions/</link>
					<comments>https://www.bram.us/2026/03/02/animating-css-position-area-with-view-transitions/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Mon, 02 Mar 2026 22:19:20 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[anchoring]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[view transitions]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36083</guid>

					<description><![CDATA[<p>CSS Anchor Positioning is a powerful tool, but one of the things that you cannot do natively <em>(yet)</em> is animating the <code>position-area</code> property. This blog post introduces a technique to animate <code>position-area</code> changes using View Transitions.</p>]]></description>
										<content:encoded><![CDATA[<figure><div style="width: 640px;" class="wp-video"><video class="wp-video-shortcode" id="video-36083-2" width="640" height="432" loop autoplay muted preload="metadata" controls="controls"><source type="video/mp4" src="https://www.bram.us/wordpress/wp-content/uploads/2026/03/animate-position-area.mp4?_=2" /><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/03/animate-position-area.mp4">https://www.bram.us/wordpress/wp-content/uploads/2026/03/animate-position-area.mp4</a></video></div><figcaption>Recording of <a href="https://codepen.io/bramus/full/myrJYQz/b6aace5cc714e3147f81a2a1b4ed10f4">the demo</a>.</figcaption></figure>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class=intro>
<p>CSS Anchor Positioning is a powerful tool, but one of the things that you cannot do natively <em>(yet)</em> is animating the <code>position-area</code> property. This blog post introduces a technique to animate <code>position-area</code> changes using View Transitions.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
<p>🌟 This post is about CSS Anchor Positioning. If you are not familiar with the basics of it, check out <a href="https://www.bram.us/2026/02/28/anchors-aweigh-sotb2026/">this 30-min talk of mine</a> to get up to speed.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The problem</h3>
<p>When the browser chooses one of the <code>position-try-fallbacks</code> to apply to an anchored element, the <code>position-area</code>’s <a href="https://brm.us/value-processing">Computed Value</a> changes to that chosen fallback. This change is abrupt and in response the anchored element simply jumps from the old <code>position-area</code> to the new <code>position-area</code>. This change can’t be animated using CSS because <code>position-area</code> is discretely animatable <em>(or rather: it’s currently defined as “TBD”, see <a href="https://github.com/w3c/csswg-drafts/issues/13577" target="_top">w3c/csswg-drafts#13577</a>)</em>, so the value just flips midway the transition.</p>
<pre><code class="language-css" style="tab-size: 2;">.tooltip {
	position: fixed;
	position-area: block-start;
	position-try-fallbacks: flip-block;
	transition: position-area 0.2s ease; /* This doesn’t do anything visually … */
}</code></pre>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The technique</h3>
<p>Back in 2024 I explored <a href="https://www.bram.us/2024/11/25/experiment-automatically-triggered-view-transitions-with-mutationobserver/#shortcomings">a technique to automatically trigger a View Transition whenever a CSS property changes</a>. For this I relied on <a href="https://brm.us/style-observer"><code>@bramus/style-observer</code></a> which is a <code>StyleObserver</code> that allows you to respond to Computed Value changes in JavaScript. In the <code>StyleObserver</code>’s callback, I reset the targeted element to the previously recorded value, and then start a View Transition to the new value of that property.</p>
<p>For Anchor Positioning specifically, it’s sufficient to monitor the <code>position-area</code> property, as its Computed Value changes whenever a new <code>position-try-fallback</code> gets applied. To make sure that the View Transition is started from the previously recorded <code>position-area</code>, the <code>position-try</code> must be unset temporarily at the start of the View Transition. Therefore, that property also needs to be monitored.</p>
<pre><code class="language-js" style="tab-size: 2;">import StyleObserver, { ReturnFormat, NotificationMode } from "@bramus/style-observer";

const $element = document.querySelector('.tooltip');

let isBusy = false;
const styleObserver = new StyleObserver((mutations) => {
	// Prevent double runs
	if (isBusy) return;
	isBusy = true;

	const positionArea = mutations['position-area'];
		
	// No change, do nothing
	if (!positionArea.previousValue || !positionArea.changed) {
		isBusy = false;
		return;
	}
		
	// Move the element back its old location
	// This is done by forcing the old recorded positionArea
	// but most importantly by also unsetting the `position-try`
	$element.style.positionTry = 'none';
	$element.style.positionArea = positionArea.previousValue;

	// Restore the new positions
	const t = document.startViewTransition(() => {
		$element.style.positionTry = '';
		$element.style.positionArea = '';
	});

	isBusy = false;
},
{
	properties: ['position-area', 'position-anchor', 'position-try'],
	returnFormat: ReturnFormat.OBJECT,
	notificationMode: NotificationMode.ALL,
});

styleObserver.observe($element);</code></pre>
<p>Here is <a href="https://codepen.io/bramus/full/myrJYQz/b6aace5cc714e3147f81a2a1b4ed10f4">a live demo</a> that is using this technique:</p>
<p class="codepen" data-height="600" data-pen-title="CSS Anchor Positioning: Animating `position-area` with View Transitions, powered by @bramus/style-observer" data-default-tab="result" data-slug-hash="myrJYQz" data-user="bramus" data-token="b6aace5cc714e3147f81a2a1b4ed10f4" style="height: 600px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;"><span>See the Pen <a href="https://codepen.io/bramus/pen/myrJYQz/b6aace5cc714e3147f81a2a1b4ed10f4">CSS Anchor Positioning: Animating `position-area` with View Transitions, powered by @bramus/style-observer </a> by Bramus (<a href="https://codepen.io/bramus">@bramus</a>) on <a href="https://codepen.io">CodePen</a>.</span></p>
<p><script async src="https://public.codepenassets.com/embed/index.js"></script></p>
<p>Scroll the page up and down to trigger a different <code>position-area</code> on the positioned element.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Known Issues</h3>
<p>Unfortunately there are some issues with the technique:</p>
<ol>
<li>
<p>The code does not work in Firefox.</p>
<p>I initially thought this was because of <code>position-area</code> being underspecified in the spec – it’s animation type is currently specced as “TBD” – and that Firefox therefore did not mark the property as being <a href="https://www.w3.org/TR/web-animations/#discrete">Discretely Animatable</a>. A look at Stylo’s source code tells me <a href="https://github.com/servo/stylo/blob/2f131eb1909c6193a7cfae22e49f288fe9df4fbb/style/properties/longhands.toml#L1736-L1746">it <em>is</em> defined as a discretely animatable property</a> so so that’s not the problem.</p>
<p>Digging into the code of my <code>StyleObserver</code>, I see it only picks up the initially applied value of <code>position-area</code> but no subsequent changes, even though a <code>getComputedStyle()</code> indicates that the value did change. I think there is a bug on Firefox’s end in which it does not trigger a transition when the value changes as the result of a <code>position-try-fallback</code> being chosen.</p>
<p>I have filed <a href="https://github.com/w3c/csswg-drafts/issues/13577">w3c/csswg-drafts#13577</a> at the CSSWG to fix the spec, and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=2020592">https://bugzilla.mozilla.org/show_bug.cgi?id=2020592</a> with Firefox to get the bug sorted on their end.</p>
</li>
<li>
<p>Chrome is affected by <a href="https://www.bram.us/2024/11/25/experiment-automatically-triggered-view-transitions-with-mutationobserver/#one-frame-glitch" target="_top">a 1-frame glitch</a> due to the ambiguously defined timing of <code>transitionrun</code>. <a href="https://github.com/w3c/csswg-drafts/issues/11665" target="_top">w3c/csswg-drafts#11665</a> is concerned with this.</p>
</li>
<li>
<p>In Safari, the <code>transitionrun</code> that tracks <code>position-area</code> keeps firing over and over once it has detected a change. This is fixed in Safari Technology Preview.</p>
</li>
<li>
<p>While the View Transition is running, the positioned and anchored elements can feel a bit out-of-sync. This is because of how View Transitions deal with scroll: during a scroll, VTs retarget the end <code>transform</code> of the <code>::view-transition-group()</code> pseudos, which makes them be subjected to the <code>animation-duration</code> instead of changing instantly. In <a href="https://github.com/w3c/csswg-drafts/issues/10197" target="_top">w3c/csswg-drafts#10197</a> I am throwing around ideas to get this fixed.</p>
</li>
</ol>
<p>All these issues are fixable over time, but I would say that the 1-frame glitch in Chrome is preventing this from being something that is really usable right now.</p>
<p>Also note that the anchor in the demo does not change aspect ratio when a new <code>position-area</code> gets set. If yours does, you’ll need <a href="https://jakearchibald.com/2024/view-transitions-handling-aspect-ratio-changes/">this code by Jake</a>.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/03/02/animating-css-position-area-with-view-transitions/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://www.bram.us/wordpress/wp-content/uploads/2026/03/animate-position-area.mp4" length="204323" type="video/mp4" />

			</item>
		<item>
		<title>Anchors Aweigh! (2026.02.28 @ State of the Browser)</title>
		<link>https://www.bram.us/2026/02/28/anchors-aweigh-sotb2026/</link>
					<comments>https://www.bram.us/2026/02/28/anchors-aweigh-sotb2026/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Sat, 28 Feb 2026 14:57:36 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[anchoring]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[public speaking]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36067</guid>

					<description><![CDATA[Talk on CSS Anchor Positioning]]></description>
										<content:encoded><![CDATA[<p><figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy.jpg" alt="" width="560" height="420" class="alignnone size-medium wp-image-36068" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy.jpg 2000w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy-560x420.jpg 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy-1120x841.jpg 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy-768x576.jpg 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy-1536x1153.jpg 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreigab3ac3nblbqfqxtwbh5stbpbsszke2aqbugjtr4ckvyzqdbwruy-1568x1177.jpg 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Me, on stage. Photo by <a href="https://bsky.app/profile/joshtumath.uk/post/3mfvz23jgzc2f">Josh</a></figcaption></figure>
</p>
<div class="intro">
<p>I’m currently in London, attending the wonderful <a href="https://2026.stateofthebrowser.com/">State of the Browser</a> conference. Earlier today I opened the conference with a talk about CSS Anchor Positioning.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Table of Contents</h3>
<ul>
<li><a href="#the-talk">The Talk</a></li>
<li><a href="#slides">Slides</a></li>
<li><a href="#recording">Recording/Video</a></li>
<li><a href="#thanks">Thanks!</a></li>
</ul>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#the-talk" name="the-talk">#</a> The Talk</h3>
<p>This talk was a brand new talk <em>(which I had rehearsed at PHPAntwerp just 2 days before)</em> about CSS Anchor Positioning. It’s a high-energy, fast-paced talk that packs a lot of information in ±30 minutes.</p>
<blockquote><p>We&#8217;ve all been there. You need a popover to attach to a button, but they aren&#8217;t parent/child. You end up reaching for JavaScript, calculating coordinates, and wrestling with viewport edges. CSS Anchor Positioning is a recent API designed to solve exactly this. This talk is a practical, down-to-earth look at how it works. We&#8217;ll explore the <code>anchor()</code> function, the <code>position-anchor</code> and <code>position-area</code> properties, and <code>@position-try</code> to build UIs that are truly context-aware and robust, all without the JS hacks.</p></blockquote>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#slides" name="slides">#</a> Slides</h3>
<p>The slides of my talk are <a href="https://slidr.io/bramus/anchors-aweigh-2026-02-28-state-of-the-browser">up on slidr.io</a> are embedded below:</p>
<p><iframe loading="lazy" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" allowtransparency="true" frameborder="0" id="slidrio-deck-3665" src="https://slidr.io/bramus/anchors-aweigh-2026-02-28-state-of-the-browser?embed=true" style="border:0; padding:0; margin:0; background:transparent;" width="560" height="490"><a href="https://slidr.io/bramus/anchors-aweigh-2026-02-28-state-of-the-browser" title="Anchors aweigh! (2026.02.28 @ State of the Browser)">Check out the slides</a></iframe></p>
<p>Unfortunately these exported slides don’t contain any of the slide transitions that supported the story I was bringing. Also missing are any recordings of the included demos (they’re just screenshots in the export), but you can click the links to check them out yourself.</p>
<p>All the Anchoring demos that I built are gathered in <a href="https://codepen.io/collection/gPRabN?grid_type=LIST">this CodePen Collection</a>.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#slides" name="recording">#</a> Recording/Video</h3>
<p>The talk was recorded and is embedded below:</p>
<p><iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/VD_q3x9GnZs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></p>
<p>The recording comes wiht subtitles and you can <a href="https://2026.stateofthebrowser.com/speaker/bramus-van-damme/">find a transcript on the SotB site itself</a>.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3><a href="#thanks" name="thanks">#</a> Thanks!</h3>
<p>Thanks to Web Standards London for organizing State of the Browser, with a special shout-out to <a href="https://letorey.co.uk/">Dave</a> <em>(MORE ACID!)</em>. Similar to previous years, they’ve managed to curate a nice line-up of talks, with the right balance between practical/inspirational, known/new speakers, etc – It was an honor to get to open the conference with this technical deep dive.</p>
<p><figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-560x420.jpg" alt="" width="560" height="420" class="alignnone size-medium wp-image-36072" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-560x420.jpg 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-1120x840.jpg 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-768x576.jpg 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-1536x1152.jpg 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te-1568x1176.jpg 1568w, https://www.bram.us/wordpress/wp-content/uploads/2026/02/bafkreidbk6v5abchg74wlx64vdjb2cf4gnzeeuenjfuwebqe6luguck3te.jpg 2000w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Me, on stage with a slide about the <a href="https://brm.us/imcb">IMCB</a>. Photo by <a href="https://bsky.app/profile/r8r.at/post/3mfw2eie7fs25">Mario</a></figcaption></figure>
</p>
<p>It was wonderful catching up with familiar faces <em>(Jake! Cassie! Jeremy! Zach! Keith! Luke! Ryan! Josh! PPK! Niels! Alex! Estelle! Sophie! Sara! Manuel! …)</em> and finally meeting those of you I’ve only known online until now <em>(Oliver! Pavel! James! Josh! …)</em>!</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note"><p>💁‍♂️ If you are a conference or meetup organiser, don't hesitate to <a href="https://www.bram.us/speaking-training/">contact me to come speak at your event</a>.</p></div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/02/28/anchors-aweigh-sotb2026/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Chrome 145 146 adds Experimental Support for Vertical Tabs</title>
		<link>https://www.bram.us/2026/01/16/chrome-145-adds-experimental-support-for-vertical-tabs/</link>
					<comments>https://www.bram.us/2026/01/16/chrome-145-adds-experimental-support-for-vertical-tabs/#respond</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Fri, 16 Jan 2026 09:56:23 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[google chrome]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36042</guid>

					<description><![CDATA[Vertical Tabs are available behind a flag in Chrome 145 (current beta)]]></description>
										<content:encoded><![CDATA[<div>
<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-3.png"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-3.png" alt="" width="560" height="338" class="alignnone size-medium wp-image-36045" /></a><figcaption>Chrome 145 with the tabs shown to the side (vertical tabs) and a collapsed tab bar</figcaption></figure>
</div>
<div class=intro>
<p>Vertical Tabs are available behind a flag in Chrome 145 (current Chrome Beta)</p>
</div>
<div class=update>UPDATE: The flag for this experimental feature moved up to Chrome 146 (current Chrome Canary).</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<p>The feature is available in preview behind a feature flag. First you need to flip the flag <em>(to enable the feature)</em> and then choose the vertical tabs layout <em>(to apply the feature)</em>:</p>
<ol>
<li>Navigate to <code>chrome://flags/#vertical-tabs</code></li>
<li>Set the dropdown to enabled</li>
<li>Relaunch Chrome</li>
<li>Right click the tabbar and choose “Move Tabs To The Side”</li>
</ul>
<p>Here’s screenshots:</p>
<div class="table">
<div>
<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1.jpg"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1.jpg" alt="" width="560" height="338" class="alignnone size-medium wp-image-36043" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1.jpg 2380w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-560x338.jpg 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-1120x676.jpg 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-768x463.jpg 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-1536x927.jpg 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-2048x1236.jpg 2048w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-1-1568x946.jpg 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /></a><figcaption>Chrome 145 with the flag enabled and showing the context menu to move the tabs to the side</figcaption></figure>
</div>
<div>
<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2.jpg"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2.jpg" alt="" width="560" height="338" class="alignnone size-medium wp-image-36044" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2.jpg 2380w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-560x338.jpg 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-1120x676.jpg 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-768x463.jpg 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-1536x927.jpg 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-2048x1236.jpg 2048w, https://www.bram.us/wordpress/wp-content/uploads/2026/01/chrome-vertical-tabs-2-1568x946.jpg 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /></a><figcaption>Chrome 145 with the tabs shown to the side (vertical tabs)</figcaption></figure>
</div>
</div>
<p>If you want, you can collapse the tabs bar to a minimal form, as shown at the top of this post.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/01/16/chrome-145-adds-experimental-support-for-vertical-tabs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Using 100vw is now scrollbar-aware (in Chrome 145+, under the right conditions)</title>
		<link>https://www.bram.us/2026/01/15/100vw-horizontal-overflow-no-more/</link>
					<comments>https://www.bram.us/2026/01/15/100vw-horizontal-overflow-no-more/#comments</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Wed, 14 Jan 2026 23:20:21 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[viewport]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=36008</guid>

					<description><![CDATA[<p>From Chrome 145 onwards, <code>100vw</code> will automatically subtract the size of the (vertical) scrollbar from it if you have forced the <code>html</code> element to always show a vertical scrollbar <em>(using <code>overflow[-y]: scroll</code>)</em> or if you reserve space for it <em>(using <code>scrollbar-gutter: stable</code>)</em>. The same applies to <code>vh</code> with a horizontal scrollbar, as well as all <a href="https://www.bram.us/2021/07/08/the-large-small-and-dynamic-viewports/">small, large, and dynamic variants</a>.</p>]]></description>
										<content:encoded><![CDATA[<div class=intro>
<p>From Chrome 145 onwards, <code>100vw</code> will automatically subtract the size of the (vertical) scrollbar from it if you have forced the <code>html</code> element to always show a vertical scrollbar <em>(using <code>overflow[-y]: scroll</code>)</em> or if you reserve space for it <em>(using <code>scrollbar-gutter: stable</code>)</em>. The same applies to <code>vh</code> with a horizontal scrollbar, as well as all <a href="https://www.bram.us/2021/07/08/the-large-small-and-dynamic-viewports/">small, large, and dynamic variants</a>.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The problem with CSS Viewport Units</h3>
<p>The problem with CSS Viewport Units (<code>vw</code> and friends) is that those units do not take the presence of <a href="https://developer.chrome.com/docs/css-ui/scrollbar-styling#classic_scrollbars">classic scrollbars</a> into account. So when you size something to be <code>100vw</code> wide, it is sized to fill up the entire viewport width … which can cause overflow when there is a vertical scrollbar present.</p>
<p>In the following screenshot of <a href="https://codepen.io/bramus/pen/JoKbbMy/3a99fd8bc98988298e9517120082d548">this demo</a>, the blue box is sized to be <code>100vw</code> wide. Because of the presence of the vertical scrollbar (which reduces the viewport’s final width) the blue box becomes too wide for it to fit inside the <em>“viewport with scrollbar taken into account”</em>, causing the horizontal scrollbar to appear.</p>
<figure>
<a href="https://www.bram.us/wordpress/wp-content/uploads/2026/01/100vw-chrome144.png"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/01/100vw-chrome144.png" alt="" width="560" height="301" class="alignnone size-medium wp-image-36021" /></a><figcaption>Screenshot of <a href="https://codepen.io/bramus/pen/JoKbbMy/3a99fd8bc98988298e9517120082d548">this demo</a> in Chrome 144 on macOS with classic scrollbars. Note the presence of the horizontal scrollbar.</figcaption></figure>
<p>This “pointless” horizontal scrollbar lead Šime Vidas filing <a href="https://github.com/w3c/csswg-drafts/issues/6026">this issue at the CSS Working Group</a> in 2021.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Cycles</h3>
<p>A gut reaction to this “pointless” horizontal scrollbar problem is that CSS is stupid and that viewport units should simply take the presence of the scrollbar into account. The problem there is that this can’t “just” be done, because of cycles.</p>
<p>Say you have a page that contains only a box that is sized to be <code>100vw</code> &times; <code>calc(100vh + 1px)</code>. With classic scrollbars, this results in the page overflowing in both directions. If the <code>vw</code> unit were to adjust itself by subtracting the scrollbar size in that case, you’d be stuck with the following cyclic behavior:</p>
<ol>
<li>Page is overflowing vertically (because of the <code>calc(100vh + 1px)</code>)</li>
<li>Browser shows the vertical scrollbar</li>
<li>Page is now also overflowing horizontally (because of the <code>100vw</code>)</li>
<li>Browser subtracts the scrollbar sizes from the viewport units on both axis</li>
<li>Page is no longer overflowing horizontally</li>
<li>Browser no longer shows the horizontal scrollbar</li>
<li>Browser no longer subtracts the scrollbar size from the <code>vh</code> unit after all</li>
<li>Go back to step 1</li>
</ol>
<p>So no, simply subtracting the scrollbar’s size isn’t a viable solution here.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Always-present scrollbars (or scrollbar gutters)</h3>
<p>A thing Firefox had back in the day – but that it later removed because it wasn’t interoperable <em>(<a href="https://github.com/w3c/csswg-drafts/issues/6026#issuecomment-782958789">source</a>)</em> – was that they <em>did</em> subtract the size of the scrollbar from the <code>vw</code> unit, but only when the page was set to always show its scrollbars, which could be triggered by declaring <code>overflow-y: scroll</code> on it.</p>
<p>This approach actually works, because the presence of the vertical scrollbar is unconditional in that case – it is always present.</p>
<ol>
<li>Browser shows the vertical scrollbar, regards of whether the page is currently overflowing or not</li>
<li>Subtract the scrollbar size from the <code>vw</code> unit</li>
<li>No need to show a horizontal scrollbar</li>
</ol>
<p>At the end of 2023 <a href="https://github.com/w3c/csswg-drafts/issues/6026#issuecomment-1832443514">we resolved with the CSS Working Group</a> to resolve on this behavior:</p>
<blockquote><p>RESOLVED: if <code>overflow: scroll</code> is set on the root element (not propagated from <code>body</code>), account for the default scrollbar width in the size of <code>vw</code>. […]</p></blockquote>
<p>So that means that if you have <code>overflow-y: scroll</code> declared on <code>html</code> it will take the size of the vertical scrollbar into account for determining the value of <code>100vw</code>. The same applies to the <code>vh</code>-unit and the horizontal scrollbar.</p>
<p>What is also interesting, is that with the CSS Working Group we also resolved to take <code>scrollbar-gutter: stable</code> <em>(on the root)</em> into account as a trigger for this.</p>
<blockquote><p>RESOLVED: […] Also take <code>scrollbar-gutter</code> into account on the root.</p></blockquote>
<p>My suggestion to you here would be to declare <code>scrollbar-gutter: stable</code> over <code>overflow-y: scroll</code> on <code>html</code>, because the latter always draws a scrollbar, even when not necessary.</p>
<p><mark>Translated: Put this in your CSS reset if you hadn’t done so already:</mark></p>
<pre><code class="language-css">html {
  scrollbar-gutter: stable;
}</code></pre>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Don’t break the web</h3>
<p>It took some more back and forth after the original resolution was taken because there were <a href="https://github.com/w3c/csswg-drafts/issues/6026#issuecomment-1832490269">concerns</a> that this would break existing websites: if a site already manually subtracted the (guesstimated) scrollbar-size from <code>100vw</code>, they would visually break.</p>
<p>Early 2024 I <a href="https://github.com/w3c/csswg-drafts/issues/6026#issuecomment-1919428550">ran the numbers</a> by querying <a href="https://httparchive.org/">the HTTP Archive</a> to see how many sites actually used the <code>calc(100vw - var(--scrollbar-width))</code> pattern in combination with <code>overflow: scroll</code> declared on <code>html</code>.</p>
<p>In total 12,089,606 root pages found in the HTTP Archive November 2023 Dataset were scanned. Out of those +12 million scanned pages:</p>
<ul>
<li>324,866 use the <code>calc(100vw - &lt;length&gt;)</code> pattern <em>(2.6871512603%)</em></li>
<li>4,112 have <code>overflow: scroll</code> _declared_ on the root element <em>(0.03401268825%)</em></li>
<li>72 pages use both <em>(0.0005955529072%)</em></li>
</ul>
<p>Of those 72 pages, only 38 seem to use the <code>calc()</code> expression to cater for scrollbars <em>(0.0003143195899%)</em>. The ignored 34 pages did a calculation with a <code>&lt;length&gt;</code> greater than <code>50px</code> which seems like an unlikely width for a scrollbar to have.</p>
<p>That number is <a href="https://docs.google.com/document/d/1RC-pBBvsazYfCNNUSkPqAVpSpNJ96U8trhNkfV0v9fk/edit?tab=t.0">small enough</a> to justify this breaking change to be introduced – especially because it doesn’t lead to data-loss and the origins that use it were not part of the top 1000 websites – so with the CSS Working Group we took <a href="https://github.com/w3c/csswg-drafts/issues/6026#issuecomment-1982125916">a second resolution mid 2024</a> to continue with this plan.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Scrollbar-aware viewport units are available in Chrome 145</h3>
<p>I was happy to see that last summer <a href="https://issues.chromium.org/issues/354751900">the CrBug concerned with this</a> got assigned, and that the CLs to fixing this followed soon <em>(thanks, David!)</em>. All necessary CLs landed in November / December 2025, and on the very last day of 2025 the issue got marked as fixed 🎉</p>
<p>This week Chrome 145 goes to beta. This release includes the change as described above: viewport units are now scrollbar-aware if document scrollbars are forced on the opposing axis or if space for a scrollbar is reserved.</p>
<p>In Chrome 145, the demo shown earlier renders like this, without any “pointless” horizontal scrollbar:</p>
<figure>
<a href="https://www.bram.us/wordpress/wp-content/uploads/2026/01/100vw-chrome145.png"><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/01/100vw-chrome145.png" alt="" width="560" height="301" class="alignnone size-medium wp-image-36022" /></a><figcaption>Screenshot of <a href="https://codepen.io/bramus/pen/JoKbbMy/3a99fd8bc98988298e9517120082d548">this demo</a> in Chrome 145 on macOS with classic scrollbars. Note the lack of horizontal scrollbar.</figcaption></figure>
<p>Currently, Chromium-based browsers are the only browser to support this. Unfortunately there is no CSS-only way to feature detect this change.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2026/01/15/100vw-horizontal-overflow-no-more/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>How to Find the public WebKit Bug from the Apple-internal rdar:// Bug ID</title>
		<link>https://www.bram.us/2025/12/13/finding-webkit-bugs-using-the-rdar-id/</link>
					<comments>https://www.bram.us/2025/12/13/finding-webkit-bugs-using-the-rdar-id/#comments</comments>
		
		<dc:creator><![CDATA[Bramus!]]></dc:creator>
		<pubDate>Fri, 12 Dec 2025 23:36:56 +0000</pubDate>
				<category><![CDATA[Original Content]]></category>
		<category><![CDATA[bug tracker]]></category>
		<category><![CDATA[safari]]></category>
		<category><![CDATA[webkit]]></category>
		<guid isPermaLink="false">https://www.bram.us/?p=35977</guid>

					<description><![CDATA[<p>If you read the Safari release notes – like <a href="https://developer.apple.com/documentation/safari-release-notes/safari-26_2-release-notes">the Safari 26.2 release notes</a> – you see a lot of trailing “<em>(12345678)</em>”-mentions in the list of fixed bugs. These numbers are Apple-internal bug IDs, as used within Apple’s internal bug tracker (fka?) named “Radar”</em>.</p><p>These numbers are not linked to anything because Radar is Apple-internal, so to external people these numbers are practically useless … or are they?</p>]]></description>
										<content:encoded><![CDATA[<div class="intro">
<p>If you read the Safari release notes – like <a href="https://developer.apple.com/documentation/safari-release-notes/safari-26_2-release-notes">the Safari 26.2 release notes</a> – you see a lot of trailing “<em>(12345678)</em>”-mentions in the list of fixed bugs. These numbers are Apple-internal bug IDs, as used within Apple’s internal bug tracker (fka?) named “Radar”</em>.</p>
<p>These numbers are not linked to anything because Radar is Apple-internal, so to external people these numbers are practically useless … or are they?</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>The need for more info</h3>
<p>The thing is, that I sometimes like to look up some details behind something. For example, if I see the following in the release notes, then I’d like to know what the bug exactly was, and maybe also how it got fixed.</p>
<blockquote><p>Fixed flickering of elements with slow-painting content during view transitions. (160886647)</p></blockquote>
<p>The only info you get there is the Radar ID, which is <code>160886647</code> in this example.</p>
<p>Thankfully the WebKit commits always include a mention of this Radar ID in the commit message. Additionally, the public WebKit bugs also mention it in a comment made by the “Radar WebKit Bug Importer” bot. Using the GitHub and Bugzilla search functionality, you can find your way to the commit and public bug.</p>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Finding the Git commit</h3>
<p>The Radar ID is mentioned in Git commits in the form of <code>rdar://<strong>NUMBER</strong></code>. Leveraging GitHub’s search option and limiting it to only the <code>WebKit/WebKit</code> repo and only commits, you can find the commit linked to the Radar ID.</p>
<p>For example, when <a href="https://github.com/search?q=repo%3AWebKit%2FWebKit++%27rdar%3A%2F%2F160886647%27&#038;type=commits">searching for <code>rdar://160886647</code></a>, you get this list:</p>
<figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview.png" alt="" width="560" height="234" class="alignnone size-medium wp-image-35982" srcset="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview.png 1890w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview-560x234.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview-1120x468.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview-768x321.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview-1536x642.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-overview-1568x655.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Screenshot of the GitHub search results.</figcaption></figure>
<p>The direct search URL to use is the following:</p>
<pre><code>https://github.com/search?q=repo%3AWebKit%2FWebKit++%27rdar%3A%2F%2F<strong>NUMBER</strong>%27&type=commits</code></pre>
<p>When clicking through you see all details of <a href="https://github.com/WebKit/WebKit/commit/dd03e50fba5fcbb7190a0bc4287a0a8e25a00c61">the commit</a>.</p>
<figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github.png" alt="" width="560" height="311" class="alignnone size-medium wp-image-35979" srcset="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github.png 1504w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-560x311.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-1120x623.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-github-768x427.png 768w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Screenshot of the commit on GitHub.</figcaption></figure>
<p>In both views, you can also find your way to the public WebKit bug.</p>
<div class="note">
<p>For reasons unknown to me, not all Radar IDs can be found. For example a commit for <code>163571747</code> is nowhere to be found.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Finding the public WebKit Bug</h3>
<p>In addition to finding the public WebKit Bug through the GitHub commit, you can also find it on WebKit’s public bug tracker which is powered by <a href="https://www.bugzilla.org/">Bugzilla</a>.</p>
<p>You can’t find the bug using the default search though, because the Radar ID is not mentioned in the bug report itself nor is it listed as a separate metadata field. The Radar ID can be found in an automated comment in the form of <code>&lt;rdar://problem/<strong>NUMBER</strong>&gt;</code> left by the “Radar WebKit Bug Importer” bot.</p>
<p>To find the bug on Bugzilla, you have to do an advanced search and look for <em>comments</em> that contain that specific string. For example, when <a href="https://bugs.webkit.org/buglist.cgi?bug_status=RESOLVED&#038;f1=longdesc&#038;list_id=12814258&#038;o1=equals&#038;query_format=advanced&#038;v1=%3Crdar%3A%2F%2Fproblem%2F160886647%3E">doing an advanced search for <code>rdar://problem/160886647</code></a>, you get this list:</p>
<figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview.png" alt="" width="560" height="183" class="alignnone size-medium wp-image-35988" srcset="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview.png 2284w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-560x183.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-1120x367.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-768x252.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-1536x503.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-2048x671.png 2048w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-overview-1568x514.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Screenshot of Bugzilla search results.</figcaption></figure>
<p>The direct search URL to use it the following:</p>
<pre><code>https://bugs.webkit.org/buglist.cgi?bug_status=RESOLVED&f1=longdesc&list_id=12814258&o1=equals&query_format=advanced&v1=%3Crdar%3A%2F%2Fproblem%2F<strong>NUMBER</strong>%3E</code></pre>
<p>From the list, we can click through to <a href="https://bugs.webkit.org/show_bug.cgi?id=299091">the bug itself</a>, where you can indeed see the comment by the “Radar WebKit Bug Importer”:</p>
<figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla.png" alt="" width="560" height="528" class="alignnone size-medium wp-image-35980" srcset="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla.png 1614w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-560x528.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-1120x1056.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-768x724.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-1536x1448.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-bugzilla-1568x1479.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Screenshot of public WebKit bug with the comment by the bot highlighted.</figcaption></figure>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<h3>Pro Tip: Register a custom Site Search shortcut in Chrome for easy Radar ID lookup</h3>
<p>One of the nice features of Chrome is that it allows you to define a Site Search shortcut <em>(fka Custom Search Engine)</em> by which you can search sites. The cool part is that you’re not really limited to search only, and that you can abuse these Site Search shortcuts to become more productive.</p>
<p>For example, I have a <code>rdar</code> Site Search shortcut that immediately searches the <code>WebKit/WebKit</code> repo for the <code>rdar://<strong>NUMBER</strong></code> pattern using the number I pass into it. With it, I just type <code>rdar</code> followed by a space and then the Radar ID in the Chrome address bar. Once I hit enter Chrome will navigate to the GitHub search page and find me the commit linked to that Radar ID 🙂</p>
<figure><div style="width: 640px;" class="wp-video"><video class="wp-video-shortcode" id="video-35977-3" width="640" height="390" preload="metadata" controls="controls"><source type="video/mp4" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-search.mp4?_=3" /><a href="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-search.mp4">https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-search.mp4</a></video></div><figcaption>Recording of me search for the Radar ID 160886647 from the Chrome Address Bar.</figcaption></figure>
<p>Pretty easy, right?</p>
<p>To add this to Chrome yourself, navigate to <code>chrome://settings/searchEngines</code> and find the “Site Search” section. There, click on the “Add” button to add a new Site Search short with the following details:</p>
<ul>
<li>Name: <code>Radar</code></li>
<li>Shortcut: <code>rdar</code></li>
<li>URL: <code>https://github.com/search?q=repo%3AWebKit%2FWebKit++%27rdar%3A%2F%2F<strong>%s</strong>%27&type=commits</code></li>
</ul>
<p>The <em>Name</em> field doesn’t really matter there, but the <em>Shortcut</em> is the keyword that you will be typing in your address bar followed by a space.</p>
<figure><img loading="lazy" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search.png" alt="" width="560" height="407" class="alignnone size-medium wp-image-35992" srcset="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search.png 1832w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search-560x407.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search-1120x814.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search-768x558.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search-1536x1117.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-chrome-site-search-1568x1140.png 1568w" sizes="auto, (max-width: 560px) 100vw, 560px" /><figcaption>Screenshot of the Site Search shortcut configured in Chrome.</figcaption></figure>
<div class="note">
<p>I have use the URL to search GitHub here. You can also use the WebKit link I provided above. Be sure to replace <code>NUMBER</code> in it by <code>%s</code>, which is what Chrome will replace by the “search term” when using it.</p>
</div>
<p style="text-align: center; font-size: 28px; font-family: 'times new roman', times; margin: 3em 0;">~</p>
<div class="note">
	<p><b>🔥 Like what you see? Want to stay in the loop? Here's how:</b></p>
	<ul>
            <li><a href="https://bsky.app/profile/bram.us">🦋 Follow @bram.us on Bluesky</a></li>
            <li><a href="https://bram.us/feed">🔸 Follow bram.us using RSS</a></li>
	</ul>
	<p>I can also be found on <a href="https://x.com/bramus">𝕏 Twitter</a> and <a href="https://front-end.social/@bramus">🐘 Mastodon</a> but only post there sporadically.</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.bram.us/2025/12/13/finding-webkit-bugs-using-the-rdar-id/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		<enclosure url="https://www.bram.us/wordpress/wp-content/uploads/2025/12/rdar-search.mp4" length="1280383" type="video/mp4" />

			</item>
	</channel>
</rss>
