<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Starred Articles</title>
    <description>Starred Articles</description>
    <atom:link href="https://feedbin.com/starred/56d6b0fe68e5efd3d447fd5bf328a57e.xml" rel="self" type="application/rss+xml"/>
    <link>https://feedbin.com/</link>
    <item>
      <title><![CDATA[nearest neighbor studies 1, 2, 3]]></title>
      <description><![CDATA[<p>over the past several months i've been <em>very slowly</em> working on some pixel art gouache studies based off of figure drawings i'd done of both live models at <a href="https://www.jcprintroom.com/">jc print room</a> and online. if you read my <a href="http://localhost:8080/tag/weekly-retro/">weekly retros</a> you'd have seen some peeks into my work. if not, this post is also for you. and i wanted to share photos of the process taken with my <a href="https://www.photographyblog.com/pages/reviews/reviews_samsung_nv10.php">samsung nv10</a> that i bought myself 20 years ago. i'm not trying to blog about cameras right now though.</p>
<p>tl;dr: i painted three paintings, and here are all three paintings together:</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11405.JPG" alt="photo of me making a monster face in front of my three pixel art figure paintings on the easel behind me"></p>
<p>i call this series "nearest neighbor" because i digitally render the references by scanning my sketches and scaling them down using the nearest neighbor <a href="https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms">scaling algorithm</a> which gives it a sharp pixel art output. i've been using <a href="https://www.photopea.com/">photopea</a> for this a couple of years now.</p>
<p>okay, so i'll start each study showing my original sketch and then process shots. remember, these were taken with a 20-year-old point and shoot digital camera, so do try to appreciate the dreaminess of the shots instead of lamenting the...lack of focus lol.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11397.JPG" alt="figure drawing sketch in black ink of a nude woman with one hand on her hip and the other bent with her hand touching her shoulder slightly"></p>
<p>for the first study, i took the scan of the sketch, scaled it down and then fussed with color levels so that the pixels were mostly black but there were accents of purples, greens and a couple of reds. it reminded me of when you'd put your eyes right up to a crt television screen and can see the colorful dots that are still needed to make a mostly black and white image.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11313.JPG" alt="photo of me starting the first pixel figure study on my dining room table, i'm reflected in the mirror on the wall and giving the finger (endearingly)"></p>
<p>i worked on this in december on our dining room table. i used some colors from <a href="https://www.dickblick.com/products/winsor-newton-gouache-set/">this winsor and newton student gouache set</a> that i got dirt cheap during a sale.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11325.JPG" alt="photo of the first pixel art study about halfway done, with the black outline pixels and the different collor accent pixels. you can also see my sketchbook where i'm using the excess paint to paint anotehr figure, my phone showing the digital reference, and a camera recording the process"></p>
<p>i recorded the entire process. maybe i will post a timelapse one day. i record a lot of my art practice and talk to myself so that i can go back and take notes of things i call out to myself. if i die and someone posthumously posts these videos, i will haunt them.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11328.JPG" alt="photo of the painting with a blue background pretty much finished"></p>
<p>i was painting on a livestream the other day and showed this final painting off and said i will probably go back and make the background more opaque. i like the color accents, but i wonder if it would look even cooler if the background was fully black. i will probably do this figure again in black and white with a cyan background based on my experience of the following studies.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC10003.JPG" alt="photo of a printer-paper-sized beaded tapestry where the strands are mounted from the top to a thick piece of brown board and the image is the same figure and colors as the painting"></p>
<p>oh, i also made a little beaded tapestry to see if i would enjoy doing beadwork. the answer to that is...<em>no</em>. lol. at least not right now in my life. the beads against the board sound very cool when pumagreg jumps onto the cat tree that it currently adorns. beads sure are heavy and expensive.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11396.JPG" alt="figure drawing sketch of a few nude figures in black ink. in the middle is the sketch i used of a mid-sized woman with onehttps://www.youtube.com/watch?v=MpYqt7oCXFs arm behind her back and the other arm softly rested on her hip"></p>
<p>the inspiration that sparked this series actually came from this figure sketch. this was from an online model, and it is such an elegant, simple and interesting pose. this was my favorite painting of the series so far as well.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11346.JPG" alt="photo of the painting paper with a grid drawn on it and you see the digital reference of a grayscale pixel art figure of a woman with a yellow background. you also see the camera recording the process."></p>
<p>this time i wanted to skip the color accents and just go full grayscale on the body and a bright background. i am also now realizing as i edit this photo that i recorded this painting's process too. i forget if i captured the whole thing though.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11349.JPG" alt="my painting in progress, with black pixels of the figure done. on the wall mirror you see a reflection of me giving the finger. we are at the dining room table again."></p>
<p>i'm also working at the same table with a similar setup except i only had to work with black, white and yellow gouaches. i used holbein gouaches, one of tubes i bought in berlin a very long time ago and the others were <a href="https://www.dickblick.com/products/holbein-acrylic-gouache/?Item=00811-1012">part of this 12-piece set</a> i impulse bought - a purchase that i think i would regret if i believed in the act of having regrets. 😈</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11352.JPG" alt="photo of my painting finished, with the yellow background still drying"></p>
<p>i love how this one came out. the yellow was such a good choice, and i love how it contrasts with the grayscale figure. maybe i should another bead project but with this one using the lessons i learned from the first beaded peace i did <em>[btw if you'd like to contribute to my heavy and expensive bead budget as a thank you for blogging, you can <a href="https://ko-fi.com/jennschiffer">tip me</a> and/or <a href="https://bugsrock.online/">shop my store, bugs rock</a> xoxo]</em></p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11394.JPG" alt="figure drawing sketch in pencil of a nude woman wearing a wide brimmed hat, with her arms up and two the sides holding up a long scarf. she's wearing fishnet stockings"></p>
<p>for the third study i wanted to do a larger painting and try a model that has some kind of props or accessories. this model was from an online gallery.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11365.JPG" alt="photo the grayscale figure of the above nude woman in a hat and scarf painted on paper on my easel. next to it is the print out of the digital reference, showing the background will be a medium &quot;cosmos&quot; pink."></p>
<p>i livestreamed most of the process of this painting but i didn't record the majority of it. this means that if you happened to watch any of my <a href="https://stream.place/jennschiffer.com">streamplace</a> streams, you are special and got to watch something no one else ever would. i'm still trying to figure out the best desk setup to paint live. at least it finally occurred to me that my easel is extremely adjustable in height as it's on a standing desk lol. this was way better for my back than the table!</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11372.JPG" alt="photo of the painting with all of the grayscale nude done and you can see the pencil grid"></p>
<p>another thing i wanted to try was keeping the pencil grid, in the other two paintings i erased any visible pencil grid lines. i think this is cool for inside the figure, but not so much in the background.</p>
<p><img src="https://livelaugh.blog/static/_images/post-images/2026/nearest-neighbor/SNC11382.JPG" alt="photo of the final painting, with the medium &quot;cosmo&quot; pink background painted"></p>
<p>i loved this "cosmos pink", it reminds me of a hotel i had stayed in berlin in 2019 that had a freight-like elevator this exact color. i did not like painting the accessories, the scarf in particular. it was a lot of work to keep its dimension without a lot of pay-off. it will be awhile before i try to paint this big again unless the figure is smaller.</p>
<p>🦋🍌🎀</p>
<p>when i got into painting pixel art, that was only after an entire era of me using colored pencils and gel pens to make "analog pixel art". i find painting to be way more ergonomic and affordable, with the only tradeoff being that paint is harder to control or do very precise work than with pencil or pen. even though i've been doing it awhile, i still love capturing and iterating on my process to make the practice and my work more fun. anyway here is what i learned from this series:</p>
<ol>
<li>i am not a big fan of acrylic gouache. it's hard to clean up after, and can't be reactivated.</li>
<li>monochrome figures with a high contrast background are cool to paint and look at</li>
<li>beads are heavy and expensive</li>
<li>my camera works great still even though it is 20 years old, all it needed was a new battery.</li>
<li>i shouldn't wait 7 months to share a painting</li>
</ol>
<p>at the end of the day, if any of you can take one thing out of this entire series - which is only just beginning - it's that my hoarding of peripheral cables for over 20 years has been validated, which means so has yours.</p>
<p>xoxo jenn</p>
]]></description>
      <pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://livelaugh.blog/posts/nearest-neighbor-studies/</link>
      <dc:creator>Live Laugh Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5327131066</guid>
    </item>
    <item>
      <title><![CDATA[The harness is all you need (mostly)]]></title>
      <description><![CDATA[
<p class="wp-block-paragraph">If you’re feeling overwhelmed by AI right now, you’re not alone.</p>



<p class="wp-block-paragraph">Every day it seems there is a new tool, new MCP, new model, new skill, new workflow, new feature, new social post that is some form of “Hey look! I have completely figured out AI with this one weird prompt.”</p>



<p class="wp-block-paragraph">I…don’t believe you.</p>



<p class="wp-block-paragraph">I work with AI every single day, and what I’m finding is that less is way more. It’s not about what I install or configure or trick the agent into doing that makes any real difference. That stuff is interesting, but at the end of the day it feels like gimmicks.</p>



<p class="wp-block-paragraph">I see the biggest gains in my productivity from how I use the harness and how well I understand it.</p>



<p class="wp-block-paragraph">So in this post, I’m sharing you a simple workflow that you can use to drastically improve your effectiveness with AI just by using existing features of <a href="https://github.com/features/copilot">GitHub Copilot</a>. No weird prompts. No skill everyone else seems to know about. Just the harness. The harness is all you need—mostly.</p>



<aside data-color-mode="light" data-dark-theme="dark" data-light-theme="light_dimmed" class="wp-block-group post-aside--large p-4 p-md-6 is-style-light-dimmed has-global-padding is-layout-constrained wp-block-group-is-layout-constrained is-style-light-dimmed--1" style="border-top-width:4px">
<h2 id="h-disclaimers" class="wp-block-heading h5-mktg gh-aside-title is-typography-preset-h5" style="margin-top:0">Disclaimers</h2>



<p class="wp-block-paragraph">I’m using the term “harness” interchangeably with “GitHub Copilot.” The point of this post is to keep things simple, so just know that GitHub Copilot is an agent harness.</p>



<p class="wp-block-paragraph">I don’t mean to insinuate that you won’t ever need any skills or MCPs or instructions or custom agents, etc. In fact, those things will become quite important as you progress and need to define complex workflows and automate things for your teams. In fact, I use a few throughout this blog post!</p>



<p class="wp-block-paragraph">What I am pointing out here is that you do not need any of those things to be highly successful with AI.</p>



<p class="wp-block-paragraph">Also, there is a lot of slop out there. If you don’t believe that, ask the agent to create a skill to do anything at all. It will happily oblige. Whether or not that generated skill actually works, it can be easily published to any number of skill or MCP registries.</p>
</aside>



<h2 id="h-1-pick-a-tool-any-tool" class="wp-block-heading">1. Pick a tool, any tool</h2>



<p class="wp-block-paragraph">This is an obvious one, right? Pick a tool! It’s so easy!</p>



<p class="wp-block-paragraph">But even within the GitHub Copilot family, there are a lot of options. These include the <a href="https://docs.github.com/copilot/github-copilot-in-the-cli">CLI</a>, the new <a href="https://docs.github.com/copilot/concepts/agents/github-copilot-app">GitHub Copilot app</a>, <a href="https://code.visualstudio.com/">VS Code</a>, <a href="https://visualstudio.microsoft.com/">Visual Studio</a>, and <a href="https://www.jetbrains.com/">JetBrains</a>, just to name a few.</p>



<p class="wp-block-paragraph">The good news is that these experiences are increasingly being centralized on the same harness. The details can differ by tool, but the core workflow is consistent. Learn the harness once, use it everywhere.</p>



<p class="wp-block-paragraph">That said, I do believe that learning the harness is key, and the best way to learn it is to be as close to it as possible. So if you are just starting out, I’d recommend beginning with the GitHub Copilot CLI. It’s a terminal interface, which means it’s just text. There isn’t much UI to learn. You enter a prompt. The agent does things. But the interaction is more direct, immediate, and, frankly, very satisfying.</p>



<p class="wp-block-paragraph">For this demonstration, I’ll be using the new <a href="https://github.com/features/ai/github-app">GitHub Copilot app</a>. But the harness that app uses is the exact same thing you’ll be using if you are using the GitHub Copilot CLI, Visual Studio Code and many other places you can find GitHub Copilot.</p>



<h2 id="h-2-turn-on-yolo-mode" class="wp-block-heading">2. Turn on YOLO mode</h2>



<p class="wp-block-paragraph">YOLO mode is also known as “<a href="https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli/allowing-tools">Allow All</a>.” This lets the agent execute any command without asking permission. This can vary depending on the tool you are using, but for most it is simply an <code>/allow-all</code> command in the chat. Otherwise, the agent is going to stop and wait for your approval every single time it needs to do some work.</p>



<p class="wp-block-paragraph">Agents need autonomy for you to see an increase in productivity. If you have to approve everything the agent does, you might as well just do it yourself. Besides, that’s a miserable user experience. Nobody wants to be relegated to sitting at a desk pressing the “Approve” button all day. And pressing “Approve” over and over just trains you not to read what you are being asked to approve, which defeats the purpose.</p>



<p class="wp-block-paragraph">You want to be safe with agents, though. <a href="https://x.com/mattshumer_/status/2075666403596612010">Bad things happen to good people</a>. When using YOLO mode, you don’t want to run the agent on your local machine. This is especially true when you are using them at work—data is private on your organization’s systems, and mistakes can be costly.</p>



<p class="wp-block-paragraph">Fortunately there are a bunch of options for running agents in sandboxes. An easy one to get started with is <a href="https://docs.github.com/codespaces/overview">GitHub Codespaces</a> or <a href="https://containers.dev">development containers</a>.</p>



<h2 id="h-3-start-with-a-prototype" class="wp-block-heading">3. Start with a prototype</h2>



<p class="wp-block-paragraph">One of the most magical things about AI is that you can easily prototype anything and everything up front. Historically, this was not the case. Prototyping was a full phase of a project, and were often a luxury. Now, you can make one with a prompt.</p>



<p class="wp-block-paragraph">Let’s look at a few examples.</p>



<p class="wp-block-paragraph">Let’s say we want to build a date picker web component. That seems straightforward, but it’s actually quite complex. Think of all the different things you might want to do with it.</p>



<ul class="wp-block-list">
<li>How do you navigate within the component?</li>



<li>What does the selected date look like?</li>



<li>What does a selected range look like?</li>



<li>How does the user navigate between days, months, and years?</li>
</ul>



<p class="wp-block-paragraph">Start with a simple prototype and get several variations. I usually start with something like this:</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>Give me 20 mocks for a date picker web component. Put them all in an HTML file so I can compare.</code></pre>
<a href="https://github.com/copilot?prompt=Give%20me%2020%20mocks%20for%20a%20date%20picker%20web%20component.%20Put%20them%20all%20in%20an%20HTML%20file%20so%20I%20can%20compare.&amp;utm_source=github-blog&amp;utm_medium=blog&amp;utm_campaign=organic-blog-copilot-button" class="code-copilot-btn" aria-label="Open in Copilot" target="_blank" rel="noopener noreferrer"><svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-copilot"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg></a><clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="Give me 20 mocks for a date picker web component. Put them all in an HTML file so I can compare." tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="760" width="1024" src="https://github.blog/wp-content/uploads/2026/07/prototype.png?resize=1024%2C760" alt="Twenty date picker prototypes generated in a single HTML file." class="wp-image-97742" srcset="https://github.blog/wp-content/uploads/2026/07/prototype.png?w=2033 2033w, https://github.blog/wp-content/uploads/2026/07/prototype.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/prototype.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/prototype.png?w=1024 1024w, https://github.blog/wp-content/uploads/2026/07/prototype.png?w=1536 1536w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<p class="wp-block-paragraph">In this case, the AI generated a bunch of different layouts, but one of them is a mock where it starts with the year view. That’s interesting. I would like my date picker to enable the user to zoom out to the year, then into the month, and finally to the day. These are the kinds of things you don’t consider until you see them.</p>



<p class="wp-block-paragraph">As humans, we process sensory-rich models like images, shapes, and tangible layouts much faster than dense text. Creating low-effort prototypes early on helps make complex concepts immediately intuitive.</p>



<p class="wp-block-paragraph">And this applies to non-visual tasks as well.</p>



<p class="wp-block-paragraph">For instance, if I want to add a new API endpoint, I’ll still create a visual prototype to understand the requirements and constraints before diving into the implementation.</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>Create a visual mockup of the API for this project. Add five options for how we could handle a new API endpoint that allows the user to download their analytics data.</code></pre>
<clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="Create a visual mockup of the API for this project. Add five options for how we could handle a new API endpoint that allows the user to download their analytics data." tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="760" width="1024" src="https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?resize=1024%2C760" alt="A Mermaid diagram comparing approaches for an analytics export API endpoint." class="wp-image-97743" srcset="https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?w=2033 2033w, https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?w=1024 1024w, https://github.blog/wp-content/uploads/2026/07/mermaid-1-1.png?w=1536 1536w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<p class="wp-block-paragraph">Since the GitHub Copilot app supports <a href="https://docs.github.com/copilot/tutorials/copilot-cookbook/communicate-effectively/creating-diagrams">Mermaid diagrams</a>, the agent renders this as Markdown, mapping out five different ways we could implement this API endpoint.</p>



<p class="wp-block-paragraph">When working with agents, it’s easy to forget that everything is nuanced. Prototyping helps uncover the nuances up front, so you avoid spending valuable time and tokens on rework.</p>



<p class="wp-block-paragraph">I recommend using a medium-sized model, such as GPT 5.6 Terra or Claude Sonnet, on medium reasoning for most work. I also recommend you stick with whatever model you choose here for the duration of this particular feature, bug, or enhancement. Prompt caching will save you tokens. As long as you don’t switch to a different model or reasoning level, your previous chats remain cached with the model, giving you a discount on future requests.</p>



<h2 id="h-4-plan-methodically" class="wp-block-heading">4. Plan methodically</h2>



<p class="wp-block-paragraph">Now that you know what you actually want versus what you initially thought you wanted, it’s time to plan out the implementation.</p>



<p class="wp-block-paragraph">Switch to plan mode in GitHub Copilot without starting a new session.</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>/plan Build a date picker web component. I want the user to be able to zoom in and out of years, months, and days.</code></pre>
<clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="/plan Build a date picker web component. I want the user to be able to zoom in and out of years, months, and days." tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<p class="wp-block-paragraph">That’s a pretty vague prompt, and you’ll likely have more context for the model than I do here, but this is just a demonstration. If you don’t have more context, it’s OK. That’s exactly what this step is for.</p>



<p class="wp-block-paragraph">In theory, you can get a model to one-shot anything if you compose the perfect prompt with the perfect context in the perfect order. In theory.</p>



<p class="wp-block-paragraph">But none of us can do that. Planning helps you get closer to that ideal, though, by asking all of the questions that you would need to answer yourself along the way if you were to build this out by hand:</p>



<ul class="wp-block-list">
<li>Can the start and end date be the same?</li>



<li>Are partial selections valid?</li>



<li>Should users be able to clear the date?</li>



<li>Should “today” always be a visible option?</li>



<li>Is manual entry allowed?</li>



<li>What format is the date stored in?</li>



<li>Should pasting in dates be allowed?</li>
</ul>



<p class="wp-block-paragraph">The list goes on and on. You cannot possibly think of all of these edge cases, but the model can help you identify many of them.</p>



<p class="wp-block-paragraph">You can make plan mode even more aggressive in the sheer number of questions and edge cases it asks about by installing the <a href="https://www.skills.sh/mattpocock/skills/grill-me">“grill-me”</a> skill from Matt Pocock.</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>/plan /grill-me Build a date picker web component. I want the user to be able to zoom in and out of years, months, and days.</code></pre>
<a href="https://github.com/copilot?prompt=%2Fplan%20%2Fgrill-me%20Build%20a%20date%20picker%20web%20component.%20I%20want%20the%20user%20to%20be%20able%20to%20zoom%20in%20and%20out%20of%20years%2C%20months%2C%20and%20days.&amp;utm_source=github-blog&amp;utm_medium=blog&amp;utm_campaign=organic-blog-copilot-button" class="code-copilot-btn" aria-label="Open in Copilot" target="_blank" rel="noopener noreferrer"><svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16" class="octicon octicon-copilot"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg></a><clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="/plan /grill-me Build a date picker web component. I want the user to be able to zoom in and out of years, months, and days." tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<p class="wp-block-paragraph">This planning step is critical. The point is not for you to just accept every suggestion from the AI. If you do that, you are negating the value of this planning process. The point is for you to deeply engage with the problem and guide the model. This is where your expertise comes into play.</p>



<p class="wp-block-paragraph">You can also ask the model questions back. In the screenshot below, it asks me about “non-contiguous dates.” I’m pretty sure I know what the model means here, but I’m going to ask for clarification so we’re on the same page.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="545" width="1024" src="https://github.blog/wp-content/uploads/2026/07/planmode.png?resize=1024%2C545" alt="GitHub Copilot plan mode asking clarifying questions about a date picker." class="wp-image-97744" srcset="https://github.blog/wp-content/uploads/2026/07/planmode.png?w=1387 1387w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=1024 1024w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=800 800w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=400 400w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=1032 1032w, https://github.blog/wp-content/uploads/2026/07/planmode.png?w=516 516w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<p class="wp-block-paragraph">The planning process will keep going even if you interrupt to ask clarifying questions, etc.</p>



<h2 id="h-5-implement-with-autopilot" class="wp-block-heading">5. Implement with Autopilot</h2>



<p class="wp-block-paragraph">Once the plan is finished, GitHub Copilot will likely prompt you to switch to <a href="https://docs.github.com/copilot/concepts/agents/copilot-cli/autopilot">Autopilot</a> and start implementing the plan.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="698" width="1024" src="https://github.blog/wp-content/uploads/2026/07/autopilot.png?resize=1024%2C698" alt="GitHub Copilot Autopilot implementing a plan." class="wp-image-97745" srcset="https://github.blog/wp-content/uploads/2026/07/autopilot.png?w=1251 1251w, https://github.blog/wp-content/uploads/2026/07/autopilot.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/autopilot.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/autopilot.png?w=1024 1024w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<p class="wp-block-paragraph">Autopilot is a built-in loop. It forces the model to continue working by ensuring that it has actually done what it said it would do—which in this case is completing every item in the plan.</p>



<p class="wp-block-paragraph">GitHub Copilot will automatically act as an orchestrator during this phase. If it needs to read files in the codebase, it will use the “Explore” subagent with a small model. If it deems an action relatively complex, it will likely choose the “General Purpose” subagent with a larger model. While you can get fine-grained control over orchestration in GitHub Copilot with <a href="https://docs.github.com/copilot/how-tos/copilot-cli/customize-copilot/create-custom-agents-for-cli">custom agents</a> and <a href="https://docs.github.com/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions">instructions</a>, you don’t need to do anything special to get the advantages of subagents and multimodel workflows. This works out of the box, even if you did not know that any of these things existed.</p>



<h2 id="h-6-human-review-and-iteration" class="wp-block-heading">6. Human review and iteration</h2>



<p class="wp-block-paragraph">This is where you get your dopamine hit. You get to see what the AI has created.</p>



<p class="wp-block-paragraph">But it’s likely that you won’t get exactly what you wanted. That’s normal and expected. The model cannot read your mind, and it is error-prone. Iterate with the model until you get what you actually want. Whether that’s just code or an improved UI, this is the part where your taste will decide the quality of the final product.</p>



<p class="wp-block-paragraph">For instance, here’s the date picker that GitHub Copilot gave me.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="907" width="1024" src="https://github.blog/wp-content/uploads/2026/07/datepicker-initial.png?resize=1024%2C907" alt="Initial date picker result. It shows 12 boxes with years to select from 2018-2029." class="wp-image-97746" srcset="https://github.blog/wp-content/uploads/2026/07/datepicker-initial.png?w=1047 1047w, https://github.blog/wp-content/uploads/2026/07/datepicker-initial.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/datepicker-initial.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/datepicker-initial.png?w=1024 1024w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<p class="wp-block-paragraph">Already I can see it has some issues:</p>



<ul class="wp-block-list">
<li>Animations are inconsistent</li>



<li>Text is unreadable when hovering over a selected date because of color contrast</li>



<li>It doesn’t need to say “12 YEARS” at the top.</li>



<li>When I click “Today”, it doesn’t take me to the day if I’m in the month or year view.</li>
</ul>



<p class="wp-block-paragraph">Also, I don’t <em>love</em> the design. It looks a little too much like it was created by AI—because it was!</p>



<p class="wp-block-paragraph">So here we’re just in follow-up mode. I’m going to use a CSS framework I created called <a href="https://burkeholland.github.io/postrboard-design">Postrboard</a>. I add it as a skill that just points to the CSS and tells the agent how to use it. You can feel free to install it yourself if you’d like to use it, or you can pick any other CSS framework out there that you like. Giving the model some design guidance is quite helpful, and often a CSS framework is all you need.</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>ok - we don't need a landing page here - just the component, output and settings panel in a minimal setting. Use the /postboard skill for the design and colors.

For the date picker, when I click on the day, it tries to zoom in, but can't because there is nothing to zoom to. There should be no zoom there.

It doesn't need to say "Zoom Out" at the top

When I mouse over a month or year that contains the selected day, I cannot read the hover text.

When I click "Today" it should take me to that day view, even if I'm on the month or the year.

The months don't need numbers under them and they don't need to be in boxes

Same goes for years. And it doesn't need to say "12 years" at the top."</code></pre>
<clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="ok - we don't need a landing page here - just the component, output and settings panel in a minimal setting. Use the /postboard skill for the design and colors.

For the date picker, when I click on the day, it tries to zoom in, but can't because there is nothing to zoom to. There should be no zoom there.

It doesn't need to say &quot;Zoom Out&quot; at the top

When I mouse over a month or year that contains the selected day, I cannot read the hover text.

When I click &quot;Today&quot; it should take me to that day view, even if I'm on the month or the year.

The months don't need numbers under them and they don't need to be in boxes

Same goes for years. And it doesn't need to say &quot;12 years&quot; at the top.&quot;" tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<p class="wp-block-paragraph">Notice how conversational this is. Don’t overthink it. When you’re fixing a bunch of small things like this, just give it to the model. If you’ve got the context, you’ve got the prompt.</p>



<p class="wp-block-paragraph">The most important thing is not to settle for AI output that is “good enough.” Insist on quality. Be ruthless about it. That part is still your responsibility, and knowing what a quality result is from something that isn’t is the value that you bring. No AI will ever replace your human touch and creativity.</p>



<p class="wp-block-paragraph">Here’s what my final date picker looks like. Scroll to the end of this post to see it in action.</p>



<figure class="wp-block-image size-large"><img data-recalc-dims="1" loading="lazy" decoding="async" height="710" width="1024" src="https://github.blog/wp-content/uploads/2026/07/datepicker.png?resize=1024%2C710" alt="Final date picker result. It shows a monthly calendar on the left and a view settings on the right." class="wp-image-97747" srcset="https://github.blog/wp-content/uploads/2026/07/datepicker.png?w=1545 1545w, https://github.blog/wp-content/uploads/2026/07/datepicker.png?w=300 300w, https://github.blog/wp-content/uploads/2026/07/datepicker.png?w=768 768w, https://github.blog/wp-content/uploads/2026/07/datepicker.png?w=1024 1024w, https://github.blog/wp-content/uploads/2026/07/datepicker.png?w=1536 1536w" sizes="auto, (max-width: 1000px) 100vw, 1000px"></figure>



<h2 id="h-7-rubber-duck-the-result" class="wp-block-heading">7. Rubber duck the result</h2>



<p class="wp-block-paragraph">After you’ve iterated and are happy with what you’ve created, it’s time to do a final review.</p>



<p class="wp-block-paragraph">Request a <a href="https://docs.github.com/copilot/concepts/agents/copilot-cli/rubber-duck">Rubber Duck review</a> from GitHub Copilot. You can do this just by asking for it:</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>Perform a rubber duck review on this date picker component implementation</code></pre>
<clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="Perform a rubber duck review on this date picker component implementation" tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<p class="wp-block-paragraph">In a Rubber Duck review, GitHub Copilot will request a review from a model of a different AI family. For instance, since I was using GPT 5.6 Terra, it requested a review from Sonnet. Different models were trained on different data, so they have different blind spots. A Rubber Duck review helps identify potential issues that might be missed by a single model.</p>



<p class="wp-block-paragraph">Note that you can use this at any point in this workflow. You can rubber duck prototypes. You can rubber duck plans. It all just depends on if you want a second AI review on something.</p>



<p class="wp-block-paragraph">And if you want to take this a step further, you can combine rubber duck with Autopilot to get the models to work together in a loop to improve the final result.</p>


<div class="wp-block-code-wrapper">
<pre class="wp-block-code language-plaintext"><code>/autopilot rubber duck this date picker implementation. When you have the result, review it carefully and make any necessary adjustments. Repeat the rubber duck review until both you and the reviewing model agree that the only items that remain have diminishing returns.</code></pre>
<clipboard-copy aria-label="Copy" class="code-copy-btn" data-copy-feedback="Copied!" value="/autopilot rubber duck this date picker implementation. When you have the result, review it carefully and make any necessary adjustments. Repeat the rubber duck review until both you and the reviewing model agree that the only items that remain have diminishing returns." tabindex="0" role="button"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-copy js-clipboard-copy-icon"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="octicon octicon-check js-clipboard-check-icon"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg></clipboard-copy></div>


<p class="wp-block-paragraph">After this step, you will have an even more refined result than before and will have likely identified many extra edge cases. This step does cost more tokens, but you are really battle-hardening the code. Think of it as an investment in your future self who won’t have to deal with these issues because you caught them now.</p>



<h2 id="h-8-profit" class="wp-block-heading">8. Profit</h2>



<p class="wp-block-paragraph">At this point, you’re ready to stage and commit, or move on to the next feature you want to add along with this pull request.</p>



<p class="wp-block-paragraph">I’d recommend starting a new chat session for anything you do next that doesn’t have to do with this date picker. You can think of chat sessions as being topical; if you start to diverge too much from the main topic, it’s probably time for a new session.</p>



<p class="wp-block-paragraph">Here’s the final result from my workflow building the date picker for this post.</p>



<figure class="wp-block-video"><video height="1080" style="aspect-ratio: 1520 / 1080;" width="1520" controls="" poster="https://github.blog/wp-content/uploads/2026/07/Screenshot-2026-07-24-at-11.31.41-AM.png" src="https://github.blog/wp-content/uploads/2026/07/datepicker.mp4"></video></figure>



<p class="wp-block-paragraph">I realize that this is a bit of a contrived example, but can we all just pause for a moment and marvel at what we’re able to pull off with AI now? Building a date picker used to be one of the hardest things you could try to do. Just ask any of the heroes out there who have built them.</p>



<h2 id="h-things-don-t-have-to-be-complicated" class="wp-block-heading">Things don’t have to be complicated</h2>



<p class="wp-block-paragraph">This simple workflow will be enough for most people. The simplicity also helps you multitask. It’s easier to reason about what agent is in what state and what you were doing last when you keep things simple. Your context window is limited too.</p>



<p class="wp-block-paragraph">There is so much happening in the AI space right now. There is no upper limit on the things that you can build and experiment with. You can add MCP servers, skills, instructions, and custom agents. You can set up workflows and loops, create agents that prompt agents, and stand up entire virtual dev teams.</p>



<p class="wp-block-paragraph">But keep in mind that nobody really knows what they are doing right now. We’re all figuring this out as we go. A lot of what is today’s magical incantation for AI will be tomorrow’s anti-pattern.</p>



<p class="wp-block-paragraph">Just focus on getting a repeatable, high-quality result in the simplest way that you can. Learn the harness and you’ll be just fine.</p>



<div class="wp-block-group post-content-cta has-global-padding is-layout-constrained wp-block-group-is-layout-constrained">
<p class="wp-block-paragraph"><a href="https://github.com/features/copilot">Try GitHub Copilot &gt;</a></p>
</div>

<p>The post <a href="https://github.blog/ai-and-ml/github-copilot/the-harness-is-all-you-need-mostly/">The harness is all you need (mostly)</a> appeared first on <a href="https://github.blog">The GitHub Blog</a>.</p>
]]></description>
      <pubDate>Mon, 27 Jul 2026 18:00:00 +0000</pubDate>
      <link>https://github.blog/ai-and-ml/github-copilot/the-harness-is-all-you-need-mostly/</link>
      <dc:creator>The GitHub Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5325724066</guid>
    </item>
    <item>
      <title><![CDATA[The new yet familiar CodePen&nbsp;2.0]]></title>
      <description><![CDATA[<p>CodePen v2 is now released! Because <a href="https://shoptalkshow.com/">I co-host a podcast with Chris</a> I’ve had an up-close perspective at this monumental effort over the last five years. I know how much work has gone into this release and I applaud the team for hitting the finish line.</p>
<p>At the crux of the new experience is the new and improved editor. When you first launch into the v2.0 editor, your first response will probably be “Who moved my cheese!?” New is always uncomfortable, but there’s a fix for getting back to the old familiar UI…</p>
<ul>
<li>Hit <code>⌘ + U</code> or <code>CTRL + U</code> to hide the new pen sidebar</li>
<li>Install the <a href="https://codepen.io/editor/team/CodePenTemplates/pen/0198ce76-3d55-76ae-8cf7-edf6860caa95">Classic Block Template</a> to remove ~14 lines of HTML (Optional)</li>
</ul>
<p>Okay, now hit <code>⌘ + U</code> or <code>CTRL + U</code> again, because what’s in the sidebar are all the new features and favorite parts I want to talk about.</p>
<h2>An entire file system</h2>
<p><img src="https://cdn.daverupert.com/posts/2026/slaps-roof-of-codepen.jpg" alt="slaps roof of car meme: slaps roof of codepen, this bad boy can fit over 300 files"></p>
<p>The first thing you’ll notice when entering the new CodePen editor is a file explorer. That’s right! CodePen is breaking out of its three-file constraint and allowing you to build entire multi-file and multi-page websites.</p>
<p>A not-so-secret feature of CodePen was that you could mimic a file system in CodePen by <a href="https://blog.codepen.io/documentation/adding-external-resources/#you-can-use-other-pens-as-external-resources">using other pens as external resources</a>, but referencing your custom <code>reset.css</code> by importing <code>&lt;user&gt;/pen/&lt;slug&gt;.css</code> wasn’t the JS module-like experience you come to expect from the modern web.</p>
<p>But gone are the days of 2,000 line JavaScript files, I can now <code>import foo from './foo.js'</code> like TC39 intended. I must admit that my more complex ideas were hitting the limit of what was comfortable to type into a tiny textbox. But now creating a folder of web components or JSON data files to loop through is easier than ever. I can build multi-file and multi-page prototypes (e.g. view-transitions! service-workers!) all without spinning up a local dev server. Digging up an old CodePen is much easier than finding an old folder on an old hard drive and it’s arguably the easiest way to put a custom webpage up on the internet right now.</p>
<p>Without drawing too many comparisons to competitors, CodePen at times felt a bit underpowered when compared to other online code editing environments. But those other services that spin up an entire Node.js Docker so that I can build my <code>&lt;fart-button&gt;</code> always felt like overkill to me. CodePen now occupies not just the entry-level and middle ground, but almost the entire spectrum of needs that I have for prototyping and small site building.</p>
<h2>Versioning and deployments</h2>
<p>Who amongst us hasn’t totally beefer’d up a good pen before?</p>
<p>Ruining an otherwise quality pen to idea creep or losing work to a browser refresh is always a possibility with online editors. It’s typically not world-ending for me and my …<em>ahem</em>… spartanly styled pens, but for sure results in a couple choice swear words. I imagine <a href="https://codepen.io/trending">trending tab experts</a> and newbie beginners find this even more frustrating. But now pens have a version history so you can roll back if necessary.</p>
<p>The deployments are better now too. Sometimes when building an idea, I like to send the “chromeless” version of the pen to non-developer friends or partners/clients to avoid overwhelming them with CodePen UI. This is also useful when debugging on your phone. The <code>debug/</code>URL worked, but if you’ve ever URL-hacked a private pen URL on your phone you know how un-fun that is. But now I can deploy the pen, get a nice <code>codepen.dev</code> URL or point my custom subdomain at CodePen’s DNS, and Airdop that URL to myself. A small but meaningful quality of life improvement for me.</p>
<h2>Blocks</h2>
<p>My favorite feature of the new CodePen is Blocks. One of my biggest gripes with modern web development is the amount of tooling it takes to get started. We’ve made it so damn complex to make a webpage, no wonder AI is so popular.</p>
<p>Blocks adds a visual layer to the opaque world of preprocessors, linters, and build processes. CodePen takes those processes and stack them on top of each other to form a predictable pipeline that you can easily visualize and understand how the sausage gets made. Imagine how this must feel for beginners compared to a dozen config files littering the root of your repo! Web development demystified.</p>
<p>I’ve wanted a UI like this for build processes for a long time: Gulp, Webpack, Vite, GitHub Actions, Azure DevOps Pipelines… I feel every service would be better if it had a Blocks UI. Like <a href="https://scratch.mit.edu/">Scratch</a>, but for servers. Experienced developers might even be less afraid to operate on their build tools if they had something like this.</p>
<p>I think CodePen has created something special here that most people won’t fully understand. A perfect fit for their beginner audience as well as their professional audience who don’t want to copy, paste, and manage config files to build a simple website or goofy pen.</p>
<h2>A simplified but more powerful UI</h2>
<p>A whole new product with a new file-system, versioned deployments, and customizable build chain smooshed in… yet the UI still feels familiar. It’s rare for products to evolve so intuitively. There’s more features, but everything feels more predictable. In the “classic” CodePen editor you’d have to surf through a handful of dropdowns and modals to tweak your settings and now a lot of that work is automatic. You want Sass? Add the <code>.scss</code> file extension. You want TypeScript? Add the <code>.ts</code> extension. CodePen will add all the Blocks for you with a default config that works.</p>
<p>It feels like all the configuration options are all co-located in the right place now. And the new omnibar search box is the ultimate get-out-of-jail-free card, exposing most of the features you’d need without all the hunting and pecking. Expanding the capabilities of the product while maintaining simplicity deserves some applause and I feel the team at CodePen walked this delicate line and succeeded.</p>
<h2>A couple final thoughts</h2>
<p>I’ve said it before (and I’ll say it again) I admire the team at CodePen and –despite my obvious biases– I think they’re one of the best product teams out there. <a href="https://blog.codepen.io/2026/07/23/435-the-2-0-launch-team-codepens-favorite-features/">In their podcast</a> they give you a rare, honest, behind-the-scenes look at how they are thinking about and building their product. No one even comes close to their level of transparency.</p>
<p>Probably my favorite part of Team CodePen is that they’re not afraid to tackle the hard work and pay down technical debt when needed. They stay small, sweat the details, and take their time. There’s no back-pressure from venture capital to move fast and break your things. In my view it’s software done right.</p>
<p>Because I was part of the beta, I’m already using the hell out of CodePen 2.0. I’ve built dozens of big ideas (a browser-based camera, a service-worker based declarative shadow DOM solution, a half-dozen in-browser AI chatbots, a minimum viable design system, and more) that would have been either cumbersome or next-to-impossible in the previous editor. CodePen 2.0 is already enabling me to build bigger, better, and more complex ideas without leaving the comfort of one of my favorite applications on the whole entire internet.</p>
<p>So thanks, CodePen, for the huge upgrade and congrats to the team on the launch. 🎉</p>
]]></description>
      <pubDate>Mon, 27 Jul 2026 04:50:00 +0000</pubDate>
      <link>https://daverupert.com/2026/07/codepen-2/</link>
      <dc:creator>daverupert.com</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5325066103</guid>
    </item>
    <item>
      <title><![CDATA[Book Review: Dungeon Crawler Carl by Matt Dinniman ★★⯪☆☆]]></title>
      <description><![CDATA[<img src="https://shkspr.mobi/blog/wp-content/uploads/2026/07/6E557FF2-B3E5-45E0-9077-EB254B75FDE0IMG100.jpg" alt="Book Cover." width="200" class="alignleft size-full wp-image-72370">

<p>To call this derivative is an understatement. But that's what this genre demands; rehash all your favourite media properties into something new. It's the literary equivalent of having your Transformers™ fight your He-Man© toys.</p>

<p>The plot, such as it is, features the Vogon Constructor Fleet destroying Earth due to a late beurecratic appeal, forcing people to play The Hunger Games, while a Twitch streamer narrates the action.</p>

<p>The protagonists in Andy Weir's books are boys who know enough science to save the day. The ones in Scalzi's books have enough pop-culture knowledge to save the day. Dinniman's has played enough loot-box gatcha RPGs to save the day.</p>

<p>It is mostly pretty good fun. The tropes are goofy, the gore is splattertastic and only rarely ventures into torture porn, the villains are suitably campy, and the wisecracking sidekick doesn't become <em>too</em> annoying.</p>

<p>But the prose…</p>

<p>Here's a classic scene from Red Dwarf which exemplifies the major problem I had with this book.</p>

<iframe title="Red Dwarf Rimmers Risk Diary" width="620" height="465" src="https://www.youtube.com/embed/8qPSUr3AyzI?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<p>There's only so many times you can read "Then I severed a +3 damage with poison debuff while he raised a +1 shield and used his armour of holding to neutralise the attack".  I find opening endless lootboxen a chore when I'm playing a game - reading 150 instances of "I got a bronze box with a cauldron of Mafeking, then a gold box with an enchanted ring, then…" just becomes tiresome.</p>

<p>I tried listening to the audiobook of the sequel and I just couldn't get past the endless recitation of lists about +5 to elbows and -6 to arseholes. Just unbearably tedious.</p>

<p>Look, I've never written a novel and doubt I could do any better. This has obviously found its fanbase but, regrettably, I'm not one. It was a fun enough read, but I'm not sure I can be bothered with a dozen more sequels of recycled plots and repetitive squishing of cackling enemies.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=72363&amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></description>
      <pubDate>Sun, 26 Jul 2026 11:34:33 +0000</pubDate>
      <link>https://shkspr.mobi/blog/2026/07/book-review-dungeon-crawler-carl-by-matt-dinniman/</link>
      <dc:creator>Terence Eden’s Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5324518787</guid>
    </item>
    <item>
      <title><![CDATA[CSS Infinity Use Cases]]></title>
      <description><![CDATA[
          <img style="display: none" src="https://nerdy.dev/media/css-infinity-and-beyond.jpg" alt="Buzz light year showing flying with a title showing css infinity and beyond" height="550" width="1500">
        <script type="module">
  import "https://cdn.jsdelivr.net/npm/baseline-status";
</script>

<p>CSS <code>infinity</code> (and <code>-infinity</code>!) solve some pretty specific problems elegantly, but can also just be plain fun to use.</p>
<p><baseline-status featureid="calc-constants"></baseline-status></p>
<p><small>Showing support for <code>infinity</code>, <code>-infinity</code>, <code>NaN</code>, <code>pi</code>, and <code>e</code></small></p>
<p>Lets spur creative CSS juices with <code>infinity</code> and follow a couple use cases.</p>

        <h2 id="basics-and-gotchas">
          Basics and gotchas
          <a name="basics-and-gotchas" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#basics-and-gotchas">#</a>
        </h2>
       <p><code>infinity</code> and <code>-infinity</code> need to always be wrapped in <code>calc()</code>.</p>
<p> If you want it to represent a dimension, it also needs to be multiplied by a unit like:</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">div</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  border-radius</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:inherit">  z-index</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>Not quite limited to <code>calc()</code> too, you can use it with other <code>calc()</code>-like scenarios like <code>clamp()</code>, <code>min()</code>, <code>max()</code>, etc, which we'll dig into a bit 🤘🏻</p>

        <h2 id="1.-circles-and-pills">
          1. Circles and Pills
          <a name="1.-circles-and-pills" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#1.-circles-and-pills">#</a>
        </h2>
       <p>Before <code>infinity</code>, I would have used scientific notation like <code>1e9px</code> (which is <code>1000000000px</code>) to create a "big enough" radius for circles and pills at any size. </p>
<p>But with <code>infinity</code>..!?</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.radius-full</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  border-radius</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<div class="radius-full-example">
  <style>
    .radius-full-example {
      width: 100px;
      aspect-ratio: 1;
      background: var(--text-2);
      box-shadow: var(--shadow-2);
      border-radius: calc(infinity * 1px);
    }
  </style>
</div>

<div class="pill-example">
  <style>
    .pill-example {
      width: 100px;
      aspect-ratio: 3/1;
      background: var(--text-2);
      box-shadow: var(--shadow-2);
      border-radius: calc(infinity * 1px);
    }
  </style>
</div>


        <h2 id="2.-perma-state">
          2. Perma-state
          <a name="2.-perma-state" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#2.-perma-state">#</a>
        </h2>
       <p>Setting duration <code>calc(infinity * 1s)</code> makes an animation/transition so long that it appears to be frozen ⛄️</p>
<p>Hover over those boxes and the duration will be pinned to happen over infinity seconds, sticking the state for the duration of the page's life.</p>
<div class="perma-state-example">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<style>
  .perma-state-example {
    --size: min(3vi, 3ch);
    display: grid;
    grid: var(--size) var(--size) var(--size) / auto-flow var(--size);
    gap: 5px;

    > div {
      border: 2px solid var(--text-2);
      transition: background calc(infinity * 1s) ease;

      &:hover {
        background: var(--text-2);
        transition-duration: .2s;
      }
    }
  }
</style>

<p>I like how the duration of your hover is permanently made visible too. Visible millisecond duration via background color.</p>

        <h2 id="3.-z-index">
          3. z-index
          <a name="3.-z-index" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#3.-z-index">#</a>
        </h2>
       <p>Need to beat <code>z-index: 999999999</code> or something? </p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.tippy-top-of-stacking-context</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  z-index</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity)</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>This will max out at the largest int a browser allows: <code>2147483647</code>. Speaking of that number, you can see it by using counters like this:</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.css-max-int</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  counter-reset</span><span style="color:#ff1493">:</span><span style="color:inherit"> cssInt </span><span style="color:#4263eb">calc</span><span style="color:inherit">(infinity)</span><span style="color:#030507">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#030507">  &amp;</span><span style="color:#ff1493">:</span><span style="color:inherit">:after {</span></span>
<span class="line"><span style="color:inherit">    content: </span><span style="color:#4263eb">counter</span><span style="color:inherit">(cssInt)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">  }</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<style>
  .css-max-int {
    counter-reset: cssInt calc(infinity);

    &::after {
      font-weight: bold;
      content: " " counter(cssInt);
    }
  }
</style>

<p class="css-max-int">See!</p>


        <h2 id="4.-to-infinity-and-beyond">
          4. To infinity and beyond
          <a name="4.-to-infinity-and-beyond" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#4.-to-infinity-and-beyond">#</a>
        </h2>
       <p>Use <code>calc(infinity * 1px)</code> to remove the floor or ceiling of a clamp.</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">div</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  inline-size</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> clamp</span><span style="color:inherit">(</span></span>
<span class="line"><span style="color:#4263eb">    calc</span><span style="color:inherit">(-infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">)</span><span style="color:#be4bdb">,</span><span style="color:#868e96"> /* no floor */</span></span>
<span class="line"><span style="color:inherit">    100</span><span style="color:#ff1493">%</span><span style="color:#be4bdb">,</span><span style="color:inherit"> </span></span>
<span class="line"><span style="color:#4263eb">    calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">)   </span><span style="color:#868e96">/* no ceiling */</span></span>
<span class="line"><span style="color:inherit">  )</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>

        <h2 id="5.-freeze-view-transitions">
          5. Freeze view transitions
          <a name="5.-freeze-view-transitions" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#5.-freeze-view-transitions">#</a>
        </h2>
       <p>Make view transitions never complete. </p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#030507">::view-transition-group(</span><span style="color:#4263eb">*</span><span style="color:#030507">),</span></span>
<span class="line"><span style="color:#030507">::view-transition-old(</span><span style="color:#4263eb">*</span><span style="color:#030507">),</span></span>
<span class="line"><span style="color:#030507">::view-transition-new(</span><span style="color:#4263eb">*</span><span style="color:#030507">) {</span></span>
<span class="line"><span style="color:inherit">  animation-duration</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">s</span><span style="color:inherit">)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>The <code>::view-transition</code> tree stays in the devtools DOM, giving you an opportunity to inspect or tweak. </p>
<p>You can also freeze and inspect animations using the Animations panel in devtools.</p>

        <h2 id="6.-responsive-radius">
          6. Responsive radius
          <a name="6.-responsive-radius" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#6.-responsive-radius">#</a>
        </h2>
       <p>Another scenario where I would have reached for making a large number with scientific notation, is a responsive border radius. </p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.viewport-responsive-radius</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  border-radius</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> clamp</span><span style="color:inherit">(0</span><span style="color:#ff1493">px</span><span style="color:#be4bdb">,</span><span style="color:inherit"> (100vi - 100</span><span style="color:#ff1493">%</span><span style="color:inherit">) * infinity</span><span style="color:#be4bdb">,</span><span style="color:inherit"> 20</span><span style="color:#ff1493">px</span><span style="color:#030507">);</span></span>
<span class="line"><span style="color:#030507">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#4263eb">.container-responsive-radius</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  border-radius</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> clamp</span><span style="color:inherit">(0</span><span style="color:#ff1493">px</span><span style="color:#be4bdb">,</span><span style="color:inherit"> (100cqi - 100</span><span style="color:#ff1493">%</span><span style="color:inherit">) * infinity</span><span style="color:#be4bdb">,</span><span style="color:inherit"> 20</span><span style="color:#ff1493">px</span><span style="color:#030507">);</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>If the elements inline-size is less than 100vi, the radius will be 0. If it's greater than 100vi, the radius will be 20px.</p>
<p>There are a set of these available in <a href="https://github.com/argyleink/open-props/blob/main/src/props.borders.css#L25-L30">Open Props</a>.</p>

        <h2 id="7.-bools">
          7. Bools
          <a name="7.-bools" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#7.-bools">#</a>
        </h2>
       <p>Clamp an arbitrary number down to exactly 0 or 1, then use it:</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.flippy</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:#030507">  --solo</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> clamp</span><span style="color:inherit">(0</span><span style="color:#be4bdb">,</span><span style="color:inherit"> (2 - sibling-count()</span><span style="color:#030507">) * </span><span style="color:inherit">infinity</span><span style="color:#030507">, 1);</span></span>
<span class="line"><span style="color:#030507">  --crowded</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> clamp</span><span style="color:inherit">(0</span><span style="color:#be4bdb">,</span><span style="color:inherit"> (sibling-count() - 5</span><span style="color:#030507">) * </span><span style="color:inherit">infinity</span><span style="color:#030507">, 1);</span></span>
<span class="line"></span>
<span class="line"><span style="color:inherit">  flex-grow</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(1 </span><span style="color:#ff1493">-</span><span style="color:#4263eb"> var</span><span style="color:inherit">(--solo))</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:inherit">  font-size</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(1</span><span style="color:#ff1493">rem</span><span style="color:#ff1493"> -</span><span style="color:#4263eb"> var</span><span style="color:inherit">(--crowded) </span><span style="color:#ff1493">*</span><span style="color:inherit"> 0.125</span><span style="color:#ff1493">rem</span><span style="color:inherit">)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>One variable now can drive color, offset, opacity, gaps, whatever. </p>

        <h2 id="8.-backdrop">
          8. Backdrop
          <a name="8.-backdrop" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#8.-backdrop">#</a>
        </h2>
       <p>You can use the spread feature of <code>box-shadow</code> to create a <strong>non-interactive</strong> backdrop:</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.backdrop</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  box-shadow</span><span style="color:#ff1493">:</span><span style="color:inherit"> 0 0 0 </span><span style="color:#4263eb">calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">) #0005</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>Paint past the "entire viewport".</p>
<q class="warning">

<p>  <strong>Warning:</strong> This actually doesn't work! </p>
<p>  <a href="https://codepen.io/argyleink/pen/VYPgWGL">Try on Codepen</a></p>
<p>  <a href="https://front-end.social/@bramus/117043307620167329">Bramus explains it here</a></p>
</q>


        <h2 id="9.-scroll-trigger">
          9. Scroll Trigger
          <a name="9.-scroll-trigger" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#9.-scroll-trigger">#</a>
        </h2>
       <p>Trigger a scroll driven transition in, and never transition out.</p>
<pre class="shiki css-variables" style="background-color:#f8f9fa;padding:1em;border-radius:.5em;overflow:auto;color:#030507" tabindex="0"><code><span class="line"><span style="color:#4263eb">.nav-title</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  timeline-trigger-activation-range-end</span><span style="color:#ff1493">:</span><span style="color:#4263eb"> calc</span><span style="color:inherit">(infinity </span><span style="color:#ff1493">*</span><span style="color:inherit"> 1</span><span style="color:#ff1493">px</span><span style="color:inherit">)</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#4263eb">.product-title</span><span style="color:#030507"> {</span></span>
<span class="line"><span style="color:inherit">  animation</span><span style="color:#ff1493">:</span><span style="color:inherit"> slide-and-fade 150</span><span style="color:#ff1493">ms</span><span style="color:inherit"> ease both</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:inherit">  animation-trigger</span><span style="color:#ff1493">:</span><span style="color:inherit"> play-forwards play-backwards</span><span style="color:#030507">;</span></span>
<span class="line"><span style="color:#030507">}</span></span></code></pre>
<p>Though you'll see in the demo I use <code>scroll 100%</code> which is a specced way that feels closer to the intent I was going for, in that I wanted the range end to be the end of the scroll area.</p>
<p>Try it:</p>
<p>
          <iframe class="codepen-embed" scrolling="no" title="null" src="https://codepen.io/argyleink/embed/preview/BypVjyj?default-tab=result&amp;editable=true&amp;theme-id=43079" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
            See the Pen <a href="https://codepen.io/argyleink/embed/preview/BypVjyj"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
            on <a href="https://codepen.io">CodePen</a>.
          </iframe>
        </p>
<p>Also, just for funsies to share, here's the scroll driven version of the same navbar effect. Doesn't use infinity, but fun to try and compare scroll-triggered vs scroll-driven animation.</p>
<p>
          <iframe class="codepen-embed" scrolling="no" title="null" src="https://codepen.io/argyleink/embed/preview/zxNaGVK?default-tab=result&amp;editable=true&amp;theme-id=43079" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
            See the Pen <a href="https://codepen.io/argyleink/embed/preview/zxNaGVK"> by Adam Argyle (<a href="https://codepen.io/argyleink">@argyleink</a>)
            on <a href="https://codepen.io">CodePen</a>.
          </iframe>
        </p>

        <h2 id="thats-all">
          Thats all
          <a name="thats-all" href="https://nerdy.dev/css-infinity-use-cases?utm_source=rss#thats-all">#</a>
        </h2>
       <p>Hope you found that interesting! </p>
<p>Comment with your own creative uses of <code>infinity</code>?</p>
]]></description>
      <pubDate>Sat, 25 Jul 2026 19:57:08 +0000</pubDate>
      <link>https://nerdy.dev/css-infinity-use-cases?utm_source=rss</link>
      <dc:creator>Adam Argyle</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5324051056</guid>
    </item>
    <item>
      <title><![CDATA[]]></title>
      <description><![CDATA[Ultimately, when you install a content blocker extension, you take responsibility for any overzealousness in what they block.]]></description>
      <pubDate>Fri, 24 Jul 2026 20:57:20 +0000</pubDate>
      <link>https://daringfireball.net/2026/07/regarding_ad_blockers_and_daring_fireball</link>
      <dc:creator>Daring Fireball</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5323020941</guid>
    </item>
    <item>
      <title><![CDATA[The SLAP stack is the last server stack you'll ever need]]></title>
      <description><![CDATA[<p>I just like saying that. Clearly you should use whatever you want. “What is the SLAP stack?” you ask. Well, I just <a href="https://social.lol/@bw/116909023662989501">invented the name on Mastodon</a> a few days ago. I don’t even want to do a web search to see if it’s already taken because that will take the wind out of my sails and I won’t finish this blog post.</p>
<p>But first, there’s something you should know about me. I have this aphorism I live by: <strong>everyone is weird and everything is complicated</strong>. The former is mostly meant as a compliment. But today, I’m going to do what I can to mitigate the latter. And you know what’s complicated? Web apps. There are so many tech stacks you can choose for websites and web apps these days. They all have these dumb acronyms—LAMP, LEMP, MEAN, MERN, JAM.<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn1" id="fnref1">[1]</a></sup></p>
<p>Let’s take the LAMP stack, for example. It stands for Linux, Apache, MySQL, and PHP. This has to be the most popular server stack on the internet thanks to the popularity of WordPress, the venerable blog and website content management system that powers a whopping 40-something percent of all the websites on the internet.<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn2" id="fnref2">[2]</a></sup></p>
<p>WordPress uses PHP to reach into the MySQL database and construct whatever page is being requested. There’s quite a lot that goes on when WordPress does that, and it can be slow. It’s also pretty old (if actively maintained) software and isn’t always the funnest thing to develop sites with. I’m not saying it’s bad, just pointing out a few reasons why people might choose a different piece of software—or a different server stack altogether.</p>
<p>The rise of JavaScript on the server (i.e., Node), and frontend frameworks like React led to other stacks like MERN (MongoDB, Express, React, and Node). A lot of people are using static site generators which allows them to remove server-side complexities (although it potentially comes at a cost of introducing more complicated client-side JavaScript).</p>
<p>I do a lot of low to medium-size websites and apps. For these types of apps I’ve really come around to enjoying the SLAP stack—SQLite, Linux, Apache, and PHP. It can do it all, and doesn’t require you to run a database server. And I don’t mean to imply it’s only good for small projects. I think this stack can scale quite well considering its simplicity. Let’s talk about it.</p>
<h2 id="the-slap-stack%3A-sqlite%2C-linux%2C-apache%2C-and-php" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#the-slap-stack%3A-sqlite%2C-linux%2C-apache%2C-and-php">The SLAP stack: SQLite, Linux, Apache, and PHP</a></h2>
<p>Alright, first of all, if you thought I was going to go into a nerdy, analytical deep dive on the performance of SQLite and/or this stack let me go ahead and disappoint you right now. I am way too lazy to look into all that and get specific numbers. I’m kind of going by what I’ve read and what I’ve learned so far in the very cool course, <a href="https://databaseschool.com/series/high-performance-sqlite">High Performance SQLite</a>.</p>
<p>Let me take each technology in turn and discuss.</p>
<h3 id="sqlite" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#sqlite">SQLite</a></h3>
<p>Here is a summary of my current understanding and intuition about SQLite.</p>
<ul>
<li>The database is one file<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn3" id="fnref3">[3]</a></sup> and doesn’t need a database server</li>
<li>Easy to backup because it’s one file and has a backup command</li>
<li>Really good at reading data quickly</li>
<li>Not super great if you have a ton of concurrent writers (1,000+ writes per second maybe?)</li>
<li>Not super great if you have over a terabyte of data</li>
<li>Can (and does) easily power a website</li>
<li>For a website that will be doing a decent amount of writing, you should use the <a href="https://sqlite.org/wal.html">write-ahead logging</a> (or WAL) option</li>
</ul>
<p>I’ve only started scratching the surface of this technology so there is a ton I don’t know about it.</p>
<h3 id="linux" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#linux">Linux</a></h3>
<p>Really nothing to talk about here as many, <em>many</em> web servers are running Linux. But if you’re curious, I am running Ubuntu 26 LTS for my app in question (which I will discuss shortly).</p>
<h3 id="apache" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#apache">Apache</a></h3>
<p>I’m using Apache because it’s <a href="https://css-tricks.com/simple-boring/">simple and boring</a> and because the apps I build typically use <code>.htaccess</code> files to disallow access to certain files and directories.</p>
<h3 id="php" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#php">PHP</a></h3>
<p>I only really know two programming languages well enough to build web apps with them—PHP and JavaScript. I’m not proud of this, but it is what it is and I’m busy. My side projects need to be built with what I know. Sometimes I go vanilla PHP, other times I use my beloved <a href="https://fatfreeframework.com/">Fat Free Framework</a>.</p>
<h2 id="slap-the-app-together" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#slap-the-app-together">SLAP the app together</a></h2>
<p>All of these technologies have been around for a long time. They’re mature, well understood, and there’s plenty of information on the web about them. For me, as someone who only has nights and weekends available to mess around building apps, this is perfect. I do enjoy learning new languages. And I enjoy the act of programming, even if it’s an exercise or puzzle. But I <em>live</em> for the holistic thing itself—the website, the application, whatever it is. I love combining all the disciplines—backend code, frontend code, UI/UX, writing, art, soul—and delivering a useful app that people like. This is my passion.</p>
<hr class="thematic-break">
<p>Next up, I want to show you how you can set up the SLAP stack on a VPS (virtual private server). This is a step-up from shared hosting because it means you’re managing most aspects of the server yourself. This fact scared me for a very long time. But recently my hand was forced (my shared host’s filesystem wasn’t a good fit<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn4" id="fnref4">[4]</a></sup>), and I decided it was time to buckle up and learn this ancient skill. I’m sharing what I’ve learned here but with the disclaimer that I am a beginner at server management. You should definitely check up on anything I tell you to do here.</p>
<p>The stuff I’m going to show you should work for most SLAP stack projects, but I want to tell you briefly about my specific project, <a href="https://minimalcharactersheet.com/">Minimal Character Sheet</a>. I’ve <a href="https://blakewatson.com/journal/your-freeform-digital-character-sheet-for-dd-5e/">written about my freeform digital character sheet</a> previously but, to summarize, this is a web app that facilitates Dungeons &amp; Dragons character sheets in a format that is very similar to form-fillable PDFs. But unlike PDFs, there is plenty of  room to write. I also throw in some extra goodies like <a href="https://blakewatson.com/journal/dice-rolling-and-more-on-minimal-character-sheet/">dice rolling</a> and doing some light number-crunching (for ability and skill modifiers).</p>
<p>It runs on the SLAP stack and uses <a href="https://fatfreeframework.com/">Fat Free Framework</a>. F3 is another blog post altogether, but it’s the one PHP framework I’ve found that is as simple and noninvasive as I need.</p>
<p>The app has moderately low traffic at the moment with around 250 monthly active users. Its needs of the server are light—it’s mostly shipping JSON between the frontend and backend. It does need to send emails related to authentication, but does that with the email sending service, <a href="https://postmarkapp.com/">Postmark</a>.</p>
<p>One regret I have about this project is that, much like WordPress, all of its files are in the public HTML root. This means I have to take care to block access to certain parts of the app (like the database). I’d like to refactor the structure of the app at some point, but I have other, more pressing, things on the to-do list.</p>
<h2 id="how-to-set-up-the-slap-stack-on-a-vps" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#how-to-set-up-the-slap-stack-on-a-vps">How to set up the SLAP stack on a VPS</a></h2>
<p>I made a ton of notes while I was figuring out this process and I was doing a lot manually at first. But I narrowed things down to a few reproducible steps using scripts. That’s what I’ll share here.</p>
<p>First up, you’ll need a VPS provider. The field is huge and pricing is all over the place. It seems everyone loves <a href="https://hetzner.com/">Hetzner</a>, but they had a recent price increase and seem to be rationing the cheaper VPS options at the time of this writing. <a href="https://www.digitalocean.com/">DigitalOcean</a> is another big name here. They call their VPS products, <a href="https://www.digitalocean.com/products/droplets">Droplets</a>. This would probably be my choice, except for the fact that this self-hosting project is experimental for me and I want the cheapest reliable-ish VPS I can find. So I grabbed a $5/month no-commitment VPS from <a href="https://www.ionos.com/">IONOS</a>. I’m not recommending the service per se, but I <em>was</em> able to get a VPS up and running with decent specs at a low cost. So far so good.</p>
<h3 id="what-we%E2%80%99re-installing" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#what-we%E2%80%99re-installing">What we’re installing</a></h3>
<p>After a bit of research<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn5" id="fnref5">[5]</a></sup> I determined that these are the packages I need to install on my VPS for the simple app that I’m running.</p>
<ul>
<li><code>apache2</code>: the <a href="https://httpd.apache.org/">Apache web server</a></li>
<li><code>sqlite3</code>: the <a href="https://sqlite.org/index.html">SQLite3</a> CLI</li>
<li><code>php</code>: the latest version of php</li>
<li><code>libapache2-mod-php</code>: this is what lets Apache execute PHP files</li>
<li><code>php-sqlite3</code>: the SQLite3 extension for PHP</li>
<li><code>php-opcache</code>: automatically caches compiled PHP (not needed for PHP 8.5 turns out, but install it if you need it)</li>
<li><code>php-mbstring</code>: I don’t entirely understand this one but I think it is essentially support for UTF-8</li>
<li><code>php-curl</code>: enable a PHP app to make network requests</li>
<li><code>php-xml</code>: DOM-parsing capabilities</li>
<li><code>php-zip</code>: I have a backup script that will need to zip up files</li>
<li><code>micro</code>: this is a text editor I prefer to use because it has some mouse support, <a href="https://blakewatson.com/journal/use-your-mac-the-way-i-do-a-thought-experiment/">which is helpful for me</a></li>
</ul>
<h3 id="cloud-config" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#cloud-config">Cloud config</a></h3>
<p>After messing around with different options, I found that the quickest way to get a VPS provisioned with the stuff you want on it is to use a <a href="https://cloudbase-init.readthedocs.io/en/latest/userdata.html#cloud-config">cloud config</a>. This is a yaml file which lets you specify a configuration for your VPS much like you would do with a Dockerfile. I think most, if not all, VPS providers have some way for you to supply this configuration file during the VPS setup process.</p>
<p>Using this cloud config will let us set up a user with our ssh key(s) and disable the root user as well as password authentication. All of this is part of the <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu">basic user setup</a> you would do for a new VPS. In addition, we can specify the packages to install, set some permissions, and get our firewall up and running with a basic configuration.</p>
<pre><code class="language-yaml">#cloud-config

package_update: true

users:
  - name: YOUR_USER
    groups:
      - sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... keys go here

disable_root: true
ssh_pwauth: false

packages:
  - ufw
  - micro
  - apache2
  - php
  - libapache2-mod-php
  - sqlite3
  - php-sqlite3
  - php-mbstring
  - php-curl
  - php-xml
  - php-zip
  - php-opcache # if &lt; php v8.5

write_files:
  - path: /etc/ssh/sshd_config.d/00-hardening.conf
    owner: root:root
    permissions: "0644"
    content: |
      PasswordAuthentication no
      KbdInteractiveAuthentication no
      PubkeyAuthentication yes
      PermitRootLogin no

runcmd:
  - ufw allow OpenSSH
  - ufw allow "Apache Full"
  - ufw --force enable
  - sshd -t
  - systemctl reload ssh
  - systemctl enable --now apache2
</code></pre>
<h3 id="configuring-apache-and-getting-an-ssl-cert" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#configuring-apache-and-getting-an-ssl-cert">Configuring Apache and getting an SSL cert</a></h3>
<p>Once my VPS is running and I can SSH into it, I run the following script. It’s doing several things for me. The most important being configuring Apache and getting an SSL certificate. The script assumes that you have a domain pointing at the IP address of your VPS.</p>
<p>I tried to keep everything simple and understandable, but I was getting an error related to the JIT compiler and I added a magic line of configuration that I don’t really understand (if you are reading this and you know what that is, and a better way for me to fix it, please let me know).</p>
<p>The script expects a few variables, so you should run it like this:</p>
<pre><code class="language-bash">DOMAIN="example.com" SSH_USER="USERNAME" sh vps-setup.sh
</code></pre>
<pre><code class="language-bash">#!/bin/bash

# Stop the script if a command fails.
set -e

# This is where the website will live on the server.
WEB_ROOT="/var/www/$DOMAIN/public"

# This server does not allow PCRE to allocate the executable memory used by
# its optional JIT compiler. Regular expressions still work without it.
echo "pcre.jit=0" | sudo tee /etc/php/8.5/apache2/conf.d/99-pcre-jit.ini

# Create the website directory.
sudo mkdir -p "$WEB_ROOT"

# Create the Apache configuration file.
sudo tee "/etc/apache2/sites-available/$DOMAIN.conf" &gt; /dev/null &lt;&lt;EOF
&lt;VirtualHost *:80&gt;
    ServerName $DOMAIN

    DocumentRoot $WEB_ROOT

    &lt;Directory $WEB_ROOT&gt;
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    &lt;/Directory&gt;

    ErrorLog \${APACHE_LOG_DIR}/$DOMAIN-error.log
    CustomLog \${APACHE_LOG_DIR}/$DOMAIN-access.log combined
&lt;/VirtualHost&gt;
EOF

# Enable URL rewriting and the new website.
sudo a2enmod rewrite
sudo a2ensite "$DOMAIN.conf"

# Check the Apache configuration and reload Apache.
sudo apache2ctl configtest
sudo systemctl reload apache2

# Install Certbot and request an SSL certificate.
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache \
    --non-interactive \
    --agree-tos \
    --no-eff-email \
    --email "$CERTBOT_EMAIL" \
    -d "$DOMAIN"

# Make the data directory.
sudo mkdir -p "$WEB_ROOT/data"

# Create the temporary directory.
sudo mkdir -p "$WEB_ROOT/tmp"

# Set the normal permissions for the deployed files.
sudo chown -R "$SSH_USER:www-data" "$WEB_ROOT"
sudo find "$WEB_ROOT" -type d -exec chmod 750 {} \;
sudo find "$WEB_ROOT" -type f -exec chmod 640 {} \;

# Make data writeable by php
sudo chmod 770 "$WEB_ROOT/data"

# Make tmp directory writeable
sudo find "$WEB_ROOT/tmp" -type d -exec chmod 770 {} \;
sudo find "$WEB_ROOT/tmp" -type f -exec chmod 660 {} \;

# Deployment is handled outside this script.
echo
echo "Deploy the website to $WEB_ROOT now. Be sure to upload the environment file and the database (if it exists)."
</code></pre>
<p>The script is a bit specific to my setup. It assumes that everything is going into a <code>public</code> directory as the web root. As my app has a <code>data</code> directory inside of the web root (again, this is not a good idea, but my application does block access to that directory with a <code>.htaccess</code> file), the script creates the folder and sets the permissions for it. My PHP framework also makes use of a <code>tmp</code> folder, which I create and make writable here.</p>
<p>The Apache configuration is a very basic one that allows <code>.htaccess </code>files to work and disables automatic file listings.</p>
<p>The script also installs certbot which will grab an SSL certificate from Let’s Encrypt and create a task to renew it automatically.</p>
<p>A little message at the end is just a reminder of what to do next.</p>
<h2 id="deploying-a-website-to-your-vps" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#deploying-a-website-to-your-vps">Deploying a website to your VPS</a></h2>
<p>At this point, we need to get our website files into our web root directory. The <em>how</em> doesn’t matter so much here—use whatever’s convenient for you. The absolute quickest way to get started would probably be to use <code>rysnc</code>, a command line program for transferring files. Don’t underestimate the power of rsync! Your project may be suitable for a one-line deployment script.</p>
<pre><code class="language-bash">rsync -avz --delete mysite/ user@example.com:/var/www/example.com/public/
</code></pre>
<p>If you’re like me and you like to use a nice GUI instead of the command line, you could <a href="https://www.panic.com/transmit/">use an SFTP client</a> and manually move your website over.</p>
<p>I like to use a service called <a href="https://www.deployhq.com/">DeployHQ</a> for this purpose. It lets you set up a server connection and choose a git repo. You can set up a build pipeline if needed to run any build scripts. When you’re ready to deploy, you can choose which commit you would like to deploy, and DeployHQ will build everything and transfer the necessary files. It has some niceties like only transferring the files that have changed since the last deployment as well as a quick rollback feature if you make a mistake. If you are like me and you have a bunch of small projects, it can get a little pricey.<sup class="footnote-ref"><a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fn6" id="fnref6">[6]</a></sup></p>
<p>GitHub, GitLab, Bitbucket—they all have fancy ways to deploy stuff. I’m gonna be honest I haven’t really tried them and don’t know much about them. Point is you have a lot of options. An automatic deployment process can be really nice but isn’t necessary.</p>
<h3 id="write-ahead-logging-mode" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#write-ahead-logging-mode">Write-ahead logging mode</a></h3>
<p>I mentioned earlier that if your website is doing a lot of writing then you want to use <a href="https://sqlite.org/wal.html">write-ahead logging</a> (or WAL) for your SQLite database. According to the SQLite website, this mode is usually faster. It does not work with a networked file system, however, which is the reason I began this whole journey in the first place.</p>
<blockquote>
All processes using a database must be on the same host computer; WAL does not work over a network filesystem. This is because WAL requires all processes to share a small amount of memory and processes on separate host machines obviously cannot share memory with each other.
</blockquote>
<p class="caption">— Write-Ahead Logging, SQLite.org</p>
<p>You might do this as some sort of migration script or other or you can do it manually. If you want to use this option, you can run a command like this:</p>
<pre><code class="language-bash">sudo sqlite3 "/path/to/db.sqlite3" "PRAGMA journal_mode=WAL;"
</code></pre>
<h3 id="setting-permissions-on-your-website-files" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#setting-permissions-on-your-website-files">Setting permissions on your website files</a></h3>
<p>Once the website files are in place, we need to make sure they have the correct permissions. I have a <code>set-permissions.sh</code> script that ensures that all files start with base permissions, then elevates permissions for whatever files needs to be writeable. The script takes the same variables as the setup script.</p>
<pre><code class="language-bash">DOMAIN="example.com" SSH_USER="USERNAME" sh vps-setup.sh
</code></pre>
<p>Again, the script is a bit specific to my particular app. Feel free to change the paths to fit your own application.</p>
<pre><code class="language-bash">#!/bin/bash

# Stop the script if a command fails.
set -e

# This is where the website will live on the server.
WEB_ROOT="/var/www/$DOMAIN/public"

# Create the temporary directory.
sudo mkdir -p "$WEB_ROOT/tmp"

# Set the normal permissions for the deployed files.
sudo chown -R "$SSH_USER:www-data" "$WEB_ROOT"
sudo find "$WEB_ROOT" -type d -exec chmod 750 {} \;
sudo find "$WEB_ROOT" -type f -exec chmod 640 {} \;

# Make the data directory writable by PHP.
sudo chmod 770 "$WEB_ROOT/data"

# If the database file exists, make it writable by PHP as well.
if [ -f "$WEB_ROOT/data/db.sqlite3" ]; then
    sudo chmod 660 "$WEB_ROOT/data/db.sqlite3"
fi

# If the database file exists, make it writable by PHP as well.
if [ -f "$WEB_ROOT/data/db.sqlite3-shm" ]; then
    sudo chmod 660 "$WEB_ROOT/data/db.sqlite3-shm"
fi

# If the database file exists, make it writable by PHP as well.
if [ -f "$WEB_ROOT/data/db.sqlite3-wal" ]; then
    sudo chmod 660 "$WEB_ROOT/data/db.sqlite3-wal"
fi

# make it writable by PHP
sudo find "$WEB_ROOT/tmp" -type d -exec chmod 770 {} \;
sudo find "$WEB_ROOT/tmp" -type f -exec chmod 660 {} \;
</code></pre>
<h2 id="it-slaps" tabindex="-1"><a class="header-anchor" href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#it-slaps">It SLAPs</a></h2>
<p>That’s it! That’s the setup for a basic SLAP stack server. I think this is a cozy little setup that will hold up over the long term and keep my maintenance burden light.</p>
<p>I’m still in the experimental phase so I will write a follow-up (or edit this post) if I learn anything new, which I most assuredly will.</p>
<p>Message me on Mastodon or email me if you have any opinions about this setup, or if you want to tell me about your favorite server stack.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>AI did not write this, I just like using em dashes, okay? <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p>Its market share has stagnated for the last few years, after years of growth. <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn3" class="footnote-item"><p>Okay, technically it can be a few files if you’re using write-ahead logging (i.e., or WAL mode). <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref3" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn4" class="footnote-item"><p>My long-time shared host, NearlyFreeSpeech.NET uses a networked filesystem that, while fine for most purposes, can lead to situations <a href="https://www.sqlite.org/howtocorrupt.html#_filesystems_with_broken_or_missing_lock_implementations">where the SQLite database file becomes corrupted</a> (though I’ve found it was able to recover with minimal data loss). I needed to move to a setup that would give me a local disk with predictable file-locking. At least, that’s my vague understanding of the problem. <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref4" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn5" class="footnote-item"><p>Throughout my VPS-learning journey I used a mix of old fashioned web search, reading docs, and chatting with AI. The scripts in this article are not vibe-coded, but I did use AI to help me learn what things mean and help me assemble them. AI had no part in writing my article otherwise. <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref5" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn6" class="footnote-item"><p>I’ve been a customer for so long that I was able to get a 10 project plan at a decent price that no longer exists. Fortunately, I’m able to keep using this plan. If you’ve only got the one project, they have a free plan. But for most people I think the Solo plan should be okay. If you need more than the three projects it gives you, you might want to look elsewhere as the next plan up is a bit pricey in my opinion. <a href="https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/#fnref6" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></description>
      <pubDate>Sat, 18 Jul 2026 18:30:00 +0000</pubDate>
      <link>https://blakewatson.com/journal/the-slap-stack-is-the-last-server-stack-youll-ever-need/</link>
      <dc:creator>Personal Webpage of Blake Watson</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5312587771</guid>
    </item>
    <item>
      <title><![CDATA[Self hosting a PDS isn't hard (so far)]]></title>
      <description><![CDATA[<div class="e-content block-subcanvas"><p>Due to my compulsive need to self-host whatever I possibly can<sup id="fnref:1" class="footnote-ref">1</sup>, I decided to set up a self-hosted PDS. All that's on it so far is <a href="https://bsky.app/profile/coryd.dev">my Bluesky profile data</a>, but it was a surprisingly painless process.</p>
<p>Why bother? Well, I get to own my identity (DID) and my data. The relay and AppView remain Bluesky's, but the authoritative copy of my account is mine. I already syndicate from my site to Bluesky and controlling the PDS is the last part of this infrastructure I can reasonably control.</p>
<p>I've been bolting features on to my own CLI to manage my infrastructure for a bit<sup id="fnref:2" class="footnote-ref">2</sup>. I use <a href="https://hetzner.cloud/?ref=z8HjC9EGAYLe">Hetzner</a> for hosting and it wraps their <code>hcloud</code> CLI for my own convenience. I spun up a <code>CX23</code> box with backups turned on and then ran another CLI command to bootstrap the server.</p>
<aside>
<p>The bootstrap configures the shell, installs Tailscale and other dependencies and clones a repository that contains a shared set of infrastructure dependencies.</p>
</aside>
<p>The server is running only two things: the canonical <code>ghcr.io/bluesky-social/pds</code> container maintained by Bluesky and Caddy. I use <a href="https://forwardemail.net">forwardemail.net</a> for transactional emails.</p>
<p>With the server provisioned, I updated the <code>_atproto</code> record for my domain and added a <code>pds</code> <code>A</code> record. I use <a href="https://dnscontrol.org">DNSControl</a> to manage DNS records, so this only took a few minutes.</p>
<h2 id="the-migration">The migration</h2>
<p>Migrating my data from Bluesky's PDS to my own was the only part of this process that made me a little nervous. I needed to preserve my <code>DID</code>, followers and just over 1,000 account records. I used <a href="https://github.com/bluesky-social/goat">Bluesky's <code>goat</code> tool</a> to manage this process.</p>
<ol>
<li>I generated a recovery key using <code>goat key generate --type k256</code></li>
<li>Logged in to my old (Bluesky) account: <code>goat account login -u &lt;username&gt; -p &lt;app-password&gt; --auth-factor-token &lt;email-code&gt;</code></li>
<li>Created a PDS invite for myself using my CLI (or SSH into the box and run <code>pdsadmin create-invite-code</code>).</li>
<li>Ran the migration — it pauses and emails a PLC confirmation code that you paste into the prompt:</li>
</ol>
<pre class="language-bash"><code class="language-bash">goat account migrate <span class="token string">\
</span>    --pds-host &lt;host&gt; <span class="token string">\
</span>    --new-handle &lt;username&gt; <span class="token string">\
</span>    --new-password <span class="token string">"&lt;password&gt;"</span> <span class="token string">\
</span>    --invite-code <span class="token string">"&lt;pds-invite-code&gt;"</span>
</code></pre>
<ol start="5">
<li>Added my recovery key to the identity so it's my highest-priority rotation key (this also emails a confirmation token): <code>goat account plc add-rotation-key &lt;recovery-pubkey&gt; --first</code></li>
</ol>
<p>It ran faster than I expected and worked without any issues (shocking, I know). I syndicated a post, opened my profile in an incognito window and it loaded without issue. I also verified that DID doc <code>#atproto_pds</code> pointed at my new host and threw my private recovery key in my password manager.</p>
<h2 id="cleanup">Cleanup</h2>
<p>The last step was to add support for a custom PDS URL to my site and the PWA I built myself to browse Mastodon and Bluesky simultaneously. This consisted of a migration to update the database schema to save the URL and the corresponding field in the UI. The app passwords I use for both also needed to be recreated on the new PDS which I did from the aforementioned CLI.</p>
<p>I have yet another server running which, at this point, I'm starting to collect like trading cards. My ATProto data sits on its own server, free to be used with any apps that support it (for my purposes that's only Bluesky — for now). It was a surprisingly straightforward process and I'm thankful for that.</p>
<p>I'm still getting my head around ATProto, but I appreciate that you <em>can</em> own your own data, if not all of the application infrastructure. I was used to the Mastodon mental model where, while you can migrate between instances, not all data comes with you. You own the identity but, perhaps, not all of the content. You can't own everything in either case, for now, but it feels like we're heading in the right direction.</p>
<div class="footnotes" role="doc-endnotes"><hr><ol><li id="fn:1"><p>Except email. ↩︎</p>
</li><li id="fn:2"><p>More on that in a future post, probably. ↩︎</p>
</li></ol></div></div>]]></description>
      <pubDate>Fri, 17 Jul 2026 21:23:00 +0000</pubDate>
      <link>https://www.coryd.dev/posts/2026/self-hosting-a-pds-isnt-hard-so-far</link>
      <dc:creator>Cory Dransfeldt • Posts</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5311128186</guid>
    </item>
    <item>
      <title><![CDATA[Choose your own dark mode]]></title>
      <description><![CDATA[
<p>
<strong>Hello RSS reader!</strong>
This post contains an interactive feature.
Please visit the canonical web page for an optimal viewing experience :)
</p>
<hr>
<p>When I <a href="https://dbushell.com/2026/02/09/big-design-and-bold-ideas/">redesigned my website</a> earlier this year I removed <a href="https://dbushell.com/2020/08/05/i-have-only-gone-and-redesigned-my-website-again/#dark-mode">dark mode</a>. I never liked the colours, and the light switch toggle was <em>so 2010’s</em>.</p><p>Personally I prefer reading with a dark theme for long-form content. Dark is not my brand though and I don’t believe every website needs to support <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-color-scheme" rel="noopener noreferrer" target="_blank">colour scheme preference</a> automatically. A good browser has reader mode, I use that all the time.</p><h2 id="an-experiment">An experiment</h2><p>But what if I let my readers decide on a dark colour scheme?</p><p>Below is a colour picker doohickey that should let you experience dark mode (on this page only). I’m testing in production (for reasons) so if it’s broken come back in an hour, or update your browser. It uses the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/color" rel="noopener noreferrer" target="_blank">native colour input</a> which sucks in every browser.</p><dbushell-theme-color>  <div class="Box">    <label for="theme-color">Theme colour:</label>    <input id="theme-color" type="color" value="#00325a">    <button class="Button" type="button">      Toggle theme    </button>  </div></dbushell-theme-color><div class="Alert"><p>⚠️ <strong>Warning:</strong> expect a sudden and dramatic colour shift. Try not to flashbang yourself.</p></div><p>This is just an experiment so your colour choice will not persist. If you want to keep it, like and subscribe and @ me on the socials. Use your preferred hex code as a hashtag.</p><p>Here’s how my homepage looks with a dark blue scheme.</p><figure class="Image"><img src="https://dbushell.com/images/blog/2026/theme-color-homepage.png" alt="dbushell.com homepage with a white text on dark blue colour scheme." width="1200" height="590" decoding="async" fetchpriority="low" loading="lazy" id="--img-a95017c6"></figure><p>I reckon the duotone effect works much better than trying to invert my brand colours.</p>
<hr>
<p>
Thanks for reading! Follow me on <a href="https://dbushell.com/mastodon/">Mastodon</a> and <a href="https://dbushell.com/bluesky/">Bluesky</a>.
Subscribe to my <a href="https://dbushell.com/rss.xml">Blog</a> and <a href="https://dbushell.com/notes/rss.xml">Notes</a> or <a href="https://dbushell.com/merge/rss.xml">Combined</a> feeds.
</p>
]]></description>
      <pubDate>Thu, 16 Jul 2026 06:54:45 +0000</pubDate>
      <link>https://dbushell.com/2026/07/16/choose-your-own-dark-mode/</link>
      <dc:creator>dbushell.com (blog)</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5308380316</guid>
    </item>
    <item>
      <title><![CDATA[New orca behavior unlocked. They’ve been known to sink...]]></title>
      <description><![CDATA[
         	<p>New orca behavior unlocked. They’ve been known to sink boats and wear salmon for hats; <a href="https://www.theguardian.com/environment/2026/jul/23/orcas-ramming-prey-hard-explodes-playing-game">now they’re ramming fish so hard they explode</a>. “Orcas were observed to hold sunfish in their jaws while a second whale smashed into the target at high speed…”
</p>
 

        
        
        
    ]]></description>
      <pubDate>Thu, 23 Jul 2026 23:09:01 +0000</pubDate>
      <link>https://kottke.org/26/07/0049387-now-theyre-ramming-fish-s</link>
      <dc:creator>kottke.org</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5320584071</guid>
    </item>
    <item>
      <title><![CDATA[The Launch of CodePen 2.0]]></title>
      <description><![CDATA[
<figure class="wp-block-image size-large"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header.webp"><img decoding="async" width="1024" height="621" src="https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header-1024x621.webp" alt="" class="wp-image-21397" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header-1024x621.webp 1024w, https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header-300x182.webp 300w, https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header-768x466.webp 768w, https://blog.codepen.io/wp-content/uploads/2026/07/launch-email-1-header.webp 1422w" sizes="(max-width: 1024px) 100vw, 1024px"></a></figure>



<p class="wp-block-paragraph">If you sense a joyous exhale, that’s Team CodePen in crossing this launch milestone for the most ambitious professional project any of us has ever achieved. Today, we declare CodePen 2.0 officially launched. We do it, naturally, for you, as we set out to rebuild our online editor to better support what today’s developers want and expect. We did it in a way that makes this single editor forever extendable in its capabilities, so we can evolve with new technology.</p>



<h2 class="wp-block-heading">What Can It Do?</h2>



<p class="wp-block-paragraph">A short answer is that it can do anything that the Classic Pen and Project editors could do, and more<sup data-fn="d3420401-4557-43b3-a1e8-f42e2224c513" class="fn"><a href="https://blog.codepen.io/2026/07/23/two-point-oh/#d3420401-4557-43b3-a1e8-f42e2224c513" id="d3420401-4557-43b3-a1e8-f42e2224c513-link">1</a></sup>, combined into one editor. Even the Vue-specific editor is wholly supported by the 2.0 editor, upgrading to a modern Vite build. This means you write code and immediately see the results; the core CodePen experience. Save the Pen, share it, reference it, embed it… anything you’d do before.</p>



<p class="wp-block-paragraph">The complete list of <em>new</em> things that the 2.0 editor can do is as long as my arm. Here’s a taste of some big ones:</p>



<ul class="wp-block-list">
<li><strong>Every Pen is <a href="https://blog.codepen.io/docs/pens/deployment/">deployable</a>.</strong> With one click, you get a <code>*.codepen.app</code> subdomain website of your Pen. Update it anytime with another click, or set it to deploy on save. Easily point your DNS to it, and CodePen is now your web host for your own domains.</li>



<li><strong>Every Pen has <a href="https://blog.codepen.io/docs/pens/files/">a file system</a>.</strong> Like Projects did, only this is a much-improved true file system with a completely custom new UI designed for a smooth experience. You don’t have to use it, but it’s always there if you need it. Anything from just an extra HTML, CSS, or JavaScript file or three, to a fully blown mega multi-page website.</li>



<li><strong>Every Pen has <a href="https://arc.net/l/quote/iofzriog">a version system</a>.</strong> Make a mistake? You can roll back in time to any place you know the Pen was in a state you want it to be in.</li>



<li><strong><a href="https://arc.net/l/quote/iofzriog">Invite</a> anyone, anytime.</strong> You can invite anybody (CodePen users by username, or anyone by any email) as either an editor or viewer of a Pen. An editor will have write access to the Pen at any time, so you can work together. If you happen to be there at the same time, it all happens in real time.</li>



<li><strong>Combine technologies as you please.</strong> This is the big idea with <a href="https://blog.codepen.io/docs/pens/blocks/">Blocks</a>. Stack them up! Don’t worry about how to wire things up together. <a href="https://codepen.io/2/compiler">The CodePen Compiler</a> handles that for you.</li>



<li><strong>The screen reader accessibility</strong>&nbsp;of the editor has been vastly improved. </li>
</ul>



<p class="wp-block-paragraph">The complete list of new features is much longer, but those are some biggies!</p>



<h2 class="wp-block-heading">Podcasts</h2>



<p class="wp-block-paragraph">At one point, we literally <em>stopped</em> <a href="https://blog.codepen.io/radio/">CodePen Radio</a>, because 2.0 wasn’t out yet, and we didn’t wanna talk too much about it publicly. All we did internally was work on 2.0, so we didn’t have that much to talk about. That <a href="https://blog.codepen.io/2023/02/08/400-hiatus/">Hiatus</a> started in February 2023, and we didn’t bring it back until <a href="https://blog.codepen.io/2025/07/10/401-outgoing-email/">about a year ago</a>, when 2.0 stuff was gelling, we were close to an alpha, and things were easier to talk about.</p>



<p class="wp-block-paragraph">Now, we’ve got a bunch of podcasts that dig into 2.0 and its specific features.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="wp-block-paragraph">🎧 <a href="https://blog.codepen.io/2026/03/05/419-why-2-0/">Why 2.0?</a></p>



<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/04/21/423-2-0-templates/">🎧 2.0 Templates</a></p>



<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/06/16/429-why-codepen-rebuilt-its-realtime-service/">🎧 Why We Re-Built Our Realtime System</a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/04/08/421-view-control-of-the-2-0-editor/">🎧 The View Control of the 2.0 Editor</a></p>



<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/04/29/424-file-list-optimization/">🎧 File List Optimization</a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/04/14/422-supporting-packages/">🎧 Supporting npm Packages</a></p>



<p class="wp-block-paragraph"><a href="https://blog.codepen.io/2026/06/30/431-versions-are-deeply-integrated-into-codepen/">🎧 Versions are Deeply Integrated into 2.0</a></p>
</div>
</div>



<h2 class="wp-block-heading">The Past</h2>



<p class="wp-block-paragraph">As I said, the CodePen 2.0 project has been worked on for <em>years and years and years.</em> I thought I’d dig up some early artifacts of our thinking, planning, and playing just for fun. </p>



<figure class="wp-block-image size-large"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM.png"><img decoding="async" width="1024" height="685" src="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM-1024x685.png" alt="" class="wp-image-21359" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM-1024x685.png 1024w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM-300x201.png 300w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM-768x514.png 768w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM-1536x1028.png 1536w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-12.53.25-PM.png 1692w" sizes="(max-width: 1024px) 100vw, 1024px"></a><figcaption class="wp-element-caption">Here is the basic shape of the 2.0 editor coming together in <em>early 2021</em>. <a href="https://klare.io/">Klare Frank</a>, now at GitHub, did these early mockups. </figcaption></figure>



		<figure class="wp-block-jetpack-videopress jetpack-videopress-player" style="">
			<div class="jetpack-videopress-player__wrapper"> <iframe loading="lazy" title="VideoPress Video Player" aria-label="VideoPress Video Player" width="500" height="289" src="https://videopress.com/embed/AgegcwqS?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0" frameborder="0" allowfullscreen="" data-resize-to-parent="true" allow="clipboard-write; presentation"></iframe><script src="https://v0.wordpress.com/js/next/videopress-iframe.js?m=1770107250"></script></div>
			<figcaption>Here’s a video of early interactivity while we were just putting the componentry together for the design system.</figcaption>
			
		</figure>
		


<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM.png"><img loading="lazy" decoding="async" width="623" height="1024" src="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM-623x1024.png" alt="" class="wp-image-21362" style="aspect-ratio:0.6084030523122185;width:472px;height:auto" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM-623x1024.png 623w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM-182x300.png 182w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM-768x1263.png 768w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-08-at-8.04.05-AM.png 826w" sizes="auto, (max-width: 623px) 100vw, 623px"></a><figcaption class="wp-element-caption">The approach with “Panels” that can be opened/closed one at a time from a narrow icon sidebar on the left stayed with us the entire journey. It’s a relatively common app layout that people are familiar with from VS Code, Slack, Spotify, Discord, etc. </figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-20-at-11.08.22-AM.png"><img loading="lazy" decoding="async" width="477" height="1024" src="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-20-at-11.08.22-AM-477x1024.png" alt="" class="wp-image-21424" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-20-at-11.08.22-AM-477x1024.png 477w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-20-at-11.08.22-AM-140x300.png 140w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-20-at-11.08.22-AM.png 668w" sizes="auto, (max-width: 477px) 100vw, 477px"></a><figcaption class="wp-element-caption">Early mockup of the Privacy &amp; Collaboration Panel, before all the new options were solidified.</figcaption></figure>
</div>
</div>



<figure class="wp-block-image size-large"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM.png"><img loading="lazy" decoding="async" width="1024" height="390" src="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM-1024x390.png" alt="" class="wp-image-21358" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM-1024x390.png 1024w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM-300x114.png 300w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM-768x292.png 768w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM-1536x585.png 1536w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-07-at-11.36.45-AM.png 1944w" sizes="auto, (max-width: 1024px) 100vw, 1024px"></a><figcaption class="wp-element-caption">Here are some mockups from Stephen from last year as the UI of Versions started needing to come together. </figcaption></figure>



<figure class="wp-block-image size-large"><a href="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-scaled.png"><img loading="lazy" decoding="async" width="1024" height="613" src="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-1024x613.png" alt="" class="wp-image-21393" srcset="https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-1024x613.png 1024w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-300x180.png 300w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-768x460.png 768w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-1536x920.png 1536w, https://blog.codepen.io/wp-content/uploads/2026/07/Screenshot-2026-07-15-at-8.57.18-AM-2048x1227.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px"></a><figcaption class="wp-element-caption">It was <em>very</em> important to us to be as helpful as possible (without being annoying) when things go wrong in your code during the build process. Our diagnostics approach turned out pretty slick if you ask me.</figcaption></figure>



<p class="wp-block-paragraph">Much of the editor’s new functionality occurs in those Panels along the left. You can see on our new homepage that we showcase five of them as vertical slices of video that can move independently. Those vertical videos showcase the features fairly nicely:</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
		<figure class="wp-block-jetpack-videopress jetpack-videopress-player" style="">
			<div class="jetpack-videopress-player__wrapper"> <iframe loading="lazy" title="VideoPress Video Player" aria-label="VideoPress Video Player" width="300" height="750" src="https://videopress.com/embed/LUzjK3l8?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0" frameborder="0" allowfullscreen="" data-resize-to-parent="true" allow="clipboard-write; presentation"></iframe><script src="https://v0.wordpress.com/js/next/videopress-iframe.js?m=1770107250"></script></div>
			
			
		</figure>
		</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
		<figure class="wp-block-jetpack-videopress jetpack-videopress-player" style="">
			<div class="jetpack-videopress-player__wrapper"> <iframe loading="lazy" title="VideoPress Video Player" aria-label="VideoPress Video Player" width="300" height="750" src="https://videopress.com/embed/NkZayM0d?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0" frameborder="0" allowfullscreen="" data-resize-to-parent="true" allow="clipboard-write; presentation"></iframe><script src="https://v0.wordpress.com/js/next/videopress-iframe.js?m=1770107250"></script></div>
			
			
		</figure>
		</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
		<figure class="wp-block-jetpack-videopress jetpack-videopress-player" style="">
			<div class="jetpack-videopress-player__wrapper"> <iframe loading="lazy" title="VideoPress Video Player" aria-label="VideoPress Video Player" width="299" height="750" src="https://videopress.com/embed/qf65M1Mx?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0" frameborder="0" allowfullscreen="" data-resize-to-parent="true" allow="clipboard-write; presentation"></iframe><script src="https://v0.wordpress.com/js/next/videopress-iframe.js?m=1770107250"></script></div>
			
			
		</figure>
		</div>
</div>



<h2 class="wp-block-heading">The Future</h2>



<p class="has-luminous-vivid-amber-color has-text-color has-link-color wp-elements-0a71402779d1b482778be887da0c84e7 wp-block-paragraph"><em>Hey, where’s the AI?</em> </p>



<p class="wp-block-paragraph">Fair question, fictitious and kind reader of my mind. We know some of you don’t care about AI, and others care very much. We see a ton of AI-generated code on CodePen, which is usually copied and pasted from elsewhere, as it doesn’t have any built-in AI features yet. </p>



<p class="wp-block-paragraph">We are experimenting. We’ve got ideas. We need to do things carefully.</p>



<p class="has-luminous-vivid-amber-color has-text-color has-link-color wp-elements-2d6199f61172e516138ae81f11ffc016 wp-block-paragraph"><em>What’s going to happen with the Classic Editor?</em></p>



<p class="wp-block-paragraph">Not much, for now! You can still access and use it normally at <a href="https://codepen.io/pen">https://codepen.io/pen</a>. You may not see an obvious link in the sidebar (and other places) anymore, because we’ve launched and that’s what we want to point people toward. But it’s there if you need it.</p>



<p class="wp-block-paragraph">Perhaps, some future day, with warning, we’ll convert all Pens to 2.0. After all, part of our goal here is to consolidate on a single editor so we can focus our entire attention on constantly improving it. </p>



<p class="has-luminous-vivid-amber-color has-text-color has-link-color wp-elements-643ef6a57ad900e7993d1be2a6db49a1 wp-block-paragraph"><em>If 2.0 is all about extensibility, where are all the new processors?</em></p>



<p class="wp-block-paragraph">There are some there already. The Tailwind, Lightning CSS, and MJML Blocks are new. Vue as processed by Vite, and without using a special single-component editor, is new. Nunjucks for Pens is new. But of course, we’ll be adding many more now that we’ve gotten through launch. Got some you’d like to see? Let us know.</p>



<h2 class="wp-block-heading">A Little How-To on Replicating the Classic Experience</h2>



<p class="wp-block-paragraph">If you’re really familiar with the Classic Editor and you’re having that <em>“can it just stay the way it was?”</em> feeling, know that you can have that while using 2.0. Here’s how:</p>



<ol class="wp-block-list">
<li><strong>Use the Classic Block.</strong> This means you can write “just the part that goes into the <code>&lt;body&gt;</code>” like the Classic Editor, and the scripts/styles get connected automatically. See the config at <code>/.codepen/classic.config.json</code> to see those and other classic settings. Here’s <a href="https://codepen.io/editor/team/CodePenTemplates/pen/019c72b8-edb8-74c2-a4e3-be8663092eb2">a template</a>, which you could set to be your <a href="https://blog.codepen.io/docs/pens/templates/default-template/">default template</a>.</li>



<li><strong>Use Minimal UI</strong>. This closes the far-left sidebar and panels area, most of which represent 2.0 functionality. There is a toggle under the <strong>Controls</strong> menu, it’s findable in the Omnibar, or use ⌘-U on Mac or Control-U on PC. </li>
</ol>



<p class="wp-block-paragraph">By doing this, you get an editor that is actually <a href="https://bsky.app/profile/shaw.city/post/3mkaomuvgkc22">a bit faster</a>, is more accessible, and has capabilities to grow in case you need it. </p>



<hr class="wp-block-separator has-alpha-channel-opacity">


<ol class="wp-block-footnotes"><li id="d3420401-4557-43b3-a1e8-f42e2224c513">The small footnote to “anything” is that there are <a href="https://blog.codepen.io/docs/what-changed/#what-happened-to-x-language">some deprecated processors</a>, which were a choice made entirely because of extremely low usage.  <a href="https://blog.codepen.io/2026/07/23/two-point-oh/#d3420401-4557-43b3-a1e8-f42e2224c513-link" aria-label="Jump to footnote reference 1">↩︎</a></li></ol>]]></description>
      <pubDate>Thu, 23 Jul 2026 16:02:07 +0000</pubDate>
      <link>https://blog.codepen.io/2026/07/23/two-point-oh/</link>
      <dc:creator>CodePen</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5319955303</guid>
    </item>
    <item>
      <title><![CDATA[Saying goodbye to Zepbound]]></title>
      <description><![CDATA[<img src="https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/2026/07/IMG_6993.JPG" alt="Saying goodbye to Zepbound"><p>Last fall, I started taking Zepbound and <a href="https://a.wholelottanothing.org/zepboundin-my-first-month-on-a-glp-1/" rel="noreferrer">wrote about the first couple weeks</a> and <a href="https://a.wholelottanothing.org/random-bits-and-bobs/#updates-on-my-glp-1-experiences" rel="noreferrer">touched on it after a few months</a>. While it was a pretty life-changing at first, as I did lose 20lbs quickly, I plateaued for the next six months. During that time, I tried a higher dosage level, but unfortunately, the side effects slowly made me feel worse.</p><p>A few weeks ago, my last vial ran out and I decided to stop taking it. I might try another GLP-1 in the future—hopefully one with milder side effects—but for now I'm letting my digestive system to recover for a while.</p><h2 id="the-positives">The positives</h2><p>Zepbound was remarkable about quieting the influence of hunger on my body. For the first few weeks I almost had to schedule meals in a calendar app otherwise I would forget to eat. Eventually, a more normal appetite returned but I did always have to remember to eat light, otherwise my stomach and digestive system would be in for a world of hurt later on. The one thing it really helped out with was I no longer snacked between meals and I would rarely eat after dinner no matter how late I was up.</p><p>I am impressed that a drug could control my appetite so completely, which is an impossible thing for any magic pill to do, since so many have promised this over the years but never really delivered until now.</p><h2 id="the-not-so-great-parts">The not-so-great parts</h2><p>My digestive system slowed waaaaay down and that was a new feeling, and not a particularly good one. My old reliable schedules got out of whack as food took longer to process. When I got food poisoning twice while on Zepbound, it wrecked me for several days each time instead of being a 24hr minor inconvenience, pre-GLP-1.</p><p>After 7-8 months using Zepbound, I started getting severe intestinal gas even though I took GasX daily. I'd never had acid reflux before, but I constantly chomped on Tums to keep the heartburn at bay. Some nights, I would randomly wake up in extreme pain in the middle of the night, then belch out a 30-second burp and suddenly feel better.</p><p>I began to hate the “shot day” that you had to do once a week. Whenever I injected my shot, I would feel terrible for a day or two after. It got so bad that I started adding a few days after each week, in an attempt to avoid how bad my body would feel after each shot.</p><p>Right up until I stopped taking Zepbound, my stomach felt like the gut biome I had that kept things on an even keel had been obliterated over the past few months. I was getting an upset stomach several times a week. I was now severely lactose intolerant whenever I touched ice cream, and after spending half my life as a vegetarian, if I ate any sort of pinto or refried beans, I would feel bloated to the point I almost considered going to an ER once because I was in so much pain.</p><h2 id="whats-next">What's next</h2><p>Like I said, I might someday try a different GLP-1 or a hybrid mix of them. I disliked the needles and vials of fluid I had to interact with for the first few months of Zepbound. Later, I got injection pens through my health provider and those were a <em>million</em> times easier to use. I would be interested in the pill forms someday, since stabbing yourself gets old after a few months.</p><p>I've learned from this experiment that my eating habits were pretty bad in the past, and far too often I ate too much after dinner and late at night. On Zepbound, I was surprised at how little I should be eating at my age and with a slower metabolism. Even while on it, I stopped losing weight for months, even though it was still causing me to eat much less than I used to. I can tell that exercising is definitely more important than I thought. I never really felt I needed to force myself to eat protein though.</p><p>After a month away from it, I've gained a couple pounds back but I feel like I know what it would take to lose a few more. I'm glad I tried Zepbound and GLP-1s out, as they're pretty remarkable drugs, but I experience strong side effects with almost any prescriptions and eventually this particular drug was just too much for my system to handle.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/2026/07/F56E7907-CAE9-4E15-A425-71F2AD903F1B.jpeg" class="kg-image" alt="Saying goodbye to Zepbound" loading="lazy" width="2000" height="1500" srcset="https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/size/w600/2026/07/F56E7907-CAE9-4E15-A425-71F2AD903F1B.jpeg 600w, https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/size/w1000/2026/07/F56E7907-CAE9-4E15-A425-71F2AD903F1B.jpeg 1000w, https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/size/w1600/2026/07/F56E7907-CAE9-4E15-A425-71F2AD903F1B.jpeg 1600w, https://storage.ghost.io/c/91/15/9115e440-4672-4090-9706-8335a7b154ee/content/images/size/w2400/2026/07/F56E7907-CAE9-4E15-A425-71F2AD903F1B.jpeg 2400w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">Andy, Me, Ryan, and Dan after spending an hour beta testing The Big Walk game at Panic</span></figcaption></figure>]]></description>
      <pubDate>Thu, 23 Jul 2026 06:28:59 +0000</pubDate>
      <link>https://a.wholelottanothing.org/saying-goodbye-to-zepbound-2/</link>
      <dc:creator>A Whole Lotta Nothing</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5319281953</guid>
    </item>
    <item>
      <title><![CDATA[One Tip a Week: Stop Fighting Your Git Hooks]]></title>
      <description><![CDATA[<meta charset="utf-8"><!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]--><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="x-apple-disable-message-reformatting"><meta name="format-detection" content="telephone=no,address=no,email=no,date=no,url=no"><meta name="color-scheme" content="light"><meta name="supported-color-schemes" content="light"><title>One Tip a Week: Stop Fighting Your Git Hooks</title><!--[if mso]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--><style>
  :root { color-scheme: light; supported-color-schemes: light; }
  body { margin: 0; padding: 0; min-width: 100%!important; -ms-text-size-adjust: 100% !important; -webkit-transform: scale(1) !important; -webkit-text-size-adjust: 100% !important; -webkit-font-smoothing: antialiased !important; }
  .body { word-wrap: normal; word-spacing:normal; }
  table.mso { width: 100%; border-collapse: collapse; padding: 0; table-layout: fixed; }
  img { border: 0; outline: none; }
  table {  mso-table-lspace: 0px; mso-table-rspace: 0px; }
  td, a, span {  mso-line-height-rule: exactly; }
  #root [x-apple-data-detectors=true],
  a[x-apple-data-detectors=true],
  #MessageViewBody a { color: inherit !important; text-decoration: inherit !important; font-size: inherit !important; font-family: inherit !important; font-weight: inherit !important; line-height: inherit !important; }
  span.MsoHyperlink { color: inherit !important; mso-style-priority: 99 !important; }
  span.MsoHyperlinkFollowed { color: inherit !important; mso-style-priority: 99 !important; }
  .a { background-color:#FFFFFF; }
  .b { background-color:#030712; }
  .c  { background-color:#FFFFFF; }
  .d { background-color:#EAEBEC; }
  .d2 { background-color:#FFFFFF; }
  .d3 { background-color:#FFFFFF; }
  h1 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h2 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h3 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h4 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h5 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h6 a { text-decoration:underline;color:#0C4A6E !important;font-style:italic; }
  h1, h1 a, h2, h2 a, h3, h3 a, h4, h4 a, h5, h5 a, h6, h6 a, ul, li, ol, p, p a { margin: 0;padding: 0; }
  h1 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:28px;color:#2A2A2A;line-height:1.75;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  h2 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:24px;color:#2A2A2A;line-height:1.5;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  h3 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:20px;color:#2A2A2A;line-height:1.25;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  h4 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:18px;color:#2A2A2A;line-height:1.125;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  h5 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:16px;color:#2A2A2A;line-height:1;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  h6 { font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:400;font-size:14px;color:#2A2A2A;line-height:0.875;padding-bottom:4px;padding-top:16px;mso-margin-top-alt:16px;mso-margin-bottom-alt:4px }
  p { font-family:'Helvetica',Arial,sans-serif;font-weight:400;color:#2D2D2D;font-size:16px;line-height:1.5;padding-bottom:12px;padding-top:12px;mso-margin-top-alt:12px;mso-margin-bottom-alt:12px; }
  p a, .e a, ul a, li a, .h a, .h2 a, .h3 a { word-break:break-word;color:#0C4A6E !important;text-decoration:underline;text-decoration-color:#0C4A6E;font-style:italic; }
  p a span, .e a span, ul a span, li a span { color: inherit }
  p .bold { font-weight:bold;color:#2D2D2D; }
  p span[style*="font-size"] { line-height: 1.6; }
  .f p { font-size:12px;line-height:15px;color:#2D2D2D;padding:0; }
  .f p a { color:#2D2D2D !important; }
  .g p { font-family:'Helvetica',Arial,sans-serif;font-size:14px;line-height:20px;font-weight:normal;margin:0; }
  .g p a  { text-decoration: underline; }
  .i p { font-family:'Helvetica',Arial,sans-serif;line-height:1.5;font-size:15px;color:#2D2D2D; }
  .i p a { color:#2D2D2D !important; }
  .i2 p { font-family:'Helvetica',Arial,sans-serif;line-height:1.5;font-size:15px;color:#2D2D2D; }
  .i2 p a { color:#2D2D2D !important; }
  .i3 p { font-family:'Helvetica',Arial,sans-serif;line-height:1.8;font-size:24px;color:#2D2D2D; }
  .i3 p a { color:#2D2D2D !important; }
  .h p a { color:#0C4A6E !important; }
  .h2 p a { color:#0C4A6E !important; }
  .h3 p a { color:#0C4A6E !important; }
  .f p a, .i p a, .i2 p a, .i3 p a, .h p a, .h2 p a, .h3 p a { text-decoration:underline; }
  .j { border-top:3px solid #030712; }
  .k p { padding-left:15px;padding-bottom:0px;padding-top:6px;mso-margin-top-alt:6px;mso-margin-bottom-alt:0px;mso-margin-left-alt:15px; }
  .o { background-color:#FFFFFF;border:1px solid #F1F1F1;border-radius:5px; }
  .o p { font-family:'Helvetica',Arial,sans-serif;padding:0px;margin:0px; }
  .l p,
  .l p a, .l a { font-size:14px;line-height:20px;font-weight: bold;color:#2D2D2D;padding-bottom:6px;mso-margin-bottom-alt:6px;text-decoration:none; }
  .m p,
  .m p a { font-size:13px;line-height:18px;font-weight:400;color:#2D2D2D;padding-bottom:6px;mso-margin-bottom-alt:6px;text-decoration:none; }
  .n p,
  .n p a { font-size:12px;line-height:17px;font-weight:400;color:#2D2D2D;padding-bottom:6px;mso-margin-bottom-alt:6px;text-decoration:none; }
  .p { background-color:#FFFFFF;max-width:520px;border:1px solid #E1E8ED;border:1px solid rgba(80, 80, 80, 0.3);border-radius:5px; }
  .q { font-size:16px;font-family:Helvetica,Roboto,Calibri,sans-serif !important;border:1px solid #e1e8ed;border:1px solid rgba(80, 80, 80, 0.3);border-radius:10px;background-color:#FFFFFF; }
  .q p { font-size:16px;font-family:system-ui,Helvetica,Roboto,Calibri,sans-serif !important;color:#222222;padding:4px 0; }
  .r { border:1px solid #E1E8ED !important;border-radius:5px; }
  .s p { font-size: 14px; line-height: 17px; font-weight: 400; color: #697882; text-decoration: none; }
  .t p { font-family:'Helvetica',Arial,sans-serif;font-size:12px;line-height:1.5;font-weight:400;color:#000000;font-style:italic;padding:0px; }
  .v { border-radius:8px;border:solid 0px #030712;background-color:#030712;font-family:'Verdana',Geneva,sans-serif;color:#FFFFFF; }
  .v a { text-decoration:none;display:block;color:#FFFFFF; }
  .w p { font-size:12px;line-height:15px;font-weight:400;color:#FFFFFF; }
  .w p a { text-decoration: underline !important;color:#FFFFFF !important; }
  ul { font-family:'Helvetica',Arial,sans-serif;margin:0px 0px 0px 25px !important;padding:0px !important;color:#2D2D2D;line-height:24px;list-style-type:disc !important;font-size:16px; }
  ul ul { list-style-type:circle !important; }
  ul ul ul { list-style-type:square !important; }
  ul ul ul ul { list-style-type:disc !important; }
  ul ul ul ul ul { list-style-type:circle !important; }
  ul ul ul ul ul ul { list-style-type:square !important; }
  ul ul ul ul ul ul ul { list-style-type:disc !important; }
  ul ul ul ul ul ul ul ul { list-style-type:circle !important; }
  ul ul ul ul ul ul ul ul ul { list-style-type:square !important; }
  ul > li { font-family:'Helvetica',Arial,sans-serif;margin:10px 0px 0px 0px !important;padding: 0px 0px 0px 0px !important; color: #2D2D2D; }
  .poll-choices > li { margin: 4px 0px 0px 0px !important; }
  ol { font-family:'Helvetica',Arial,sans-serif;margin: 0px 0px 0px 25px !important;padding:0px !important;color:#2D2D2D;line-height:24px;list-style:decimal;font-size:16px; }
  ol > li { font-family:'Helvetica',Arial,sans-serif;margin:10px 0px 0px 0px !important;padding: 0px 0px 0px 0px !important; color: #2D2D2D; }
  li > p, li p { font-family:'Helvetica',Arial,sans-serif;font-size:16px;font-weight:normal;line-height:24px;padding:0px; }
  .e h3,
  .e p,
  .e span { padding-bottom:0px;padding-top:0px;mso-margin-top-alt:0px;mso-margin-bottom-alt:0px; }
  .e span,
  .e li { font-family:'Helvetica',Arial,sans-serif;font-size:16px;color:#2D2D2D;line-height:24px; }
  .rec { font-family:  ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important; }
  .rec__button:hover { background-color: #f9fafb !important; }
  .copyright a {color: inherit !important; text-decoration: none !important; font-size: inherit !important; font-family: inherit !important; font-weight: inherit !important; line-height: inherit !important;}
  .txt_social p { padding: 0; word-break: break-all; }
  .table, .table-c, .table-h { border: 1px solid #C0C0C0; }
  .table-c { padding:5px; background-color:#FFFFFF; }
  .table-c p { color: #2D2D2D; font-family:'Helvetica',Arial,sans-serif !important;overflow-wrap: break-word; }
  .table-h { padding:5px; background-color:#F1F1F1; }
  .table-h p { color: #2A2A2A; font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif !important;overflow-wrap: break-word; }
  @media only screen and (max-width:667px) {
    .aa, .w100pc { width: 100% !important; }
    .bb img { width: 100% !important; height: auto !important; max-width: none !important; }
    .cc { padding: 0px 8px !important; }
    .ee { padding-top:10px !important;padding-bottom:10px !important; }
    .ff ul, .ff ol { margin: 0px 0px 0px 10px !important;padding: 0px !important; }
    .ff li { margin:10px 0px 0px 10px !important; }
    .r {height:140px !important;}
    .s p { font-size:13px !important;line-height:15px !important; }
    .mob-hide {display:none !important;}
    .mob-show {display: block !important; width: auto !important; overflow: visible !important; float: none !important; max-height: inherit !important; line-height: inherit !important;}
    .mob-stack {width:100% !important;display:block !important;}
    .mob-w-full {width:100% !important;}
    .mob-block {display:block !important;}
    .embed-img {padding:0px 0px 12px 0px !important;}
    .socialShare {padding-top:15px !important;}
    .rec { padding-left:15px!important;padding-right:15px!important; }
    .bodyWrapper { padding:10px 4px 10px 4px !important; }
    .social-mobile {float:left !important;margin-top:10px !important;}
  }
  @media screen and (max-width: 480px) {
    u + .a .gg { width: 100% !important; width: 100vw !important; }
    .tok-heart { padding-top:75% !important; }
    .tok-play { padding-top: 250px !important; }
  }
  @media screen and (max-width: 320px) {
    .tok-heart { padding-top:65% !important; }
  }
  .u { border: 1px solid #CACACA !important; border-radius: 2px !important; background-color: #ffffff !important; padding: 0px 13px 0px 13px !important; font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif !important;font-size: 12px !important; color: #767676 !important; }
  .u a { text-decoration: none; display: block !important; color: #767676 !important; margin: 0px !important; }
  .u span, .u img { color: #767676 !important;margin:0px !important; max-height:32px !important;background-color:#ffffff !important; }
</style><!--[if mso]><style type="text/css">
    h1, h2, h3, h4, h5, h6 {font-family: Arial, sans-serif !important;}
    body, table, td, p, a, span {font-family: Arial, sans-serif !important;}
    sup { font-size: 100% !important;vertical-align: .5em !important;mso-text-raise: -1.5% !important;line-height: 0 !important; }
    ul { margin-left:0px !important; margin-right:10px !important; margin-top:20px !important; margin-bottom:20px !important; }
    ul li { margin-left: 0px !important; mso-special-format: decimal; }
    ol { margin-left:0px !important; margin-right:10px !important; margin-top:20px !important; margin-bottom:20px !important; }
    ol li { margin-left: 0px !important; mso-special-format: decimal; }
    li.listItem { margin-left:15px !important; margin-top:0px !important; }
    .paddingDesktop { padding: 10px 0 !important; }
    .edm_outlooklist { margin-left: -20px !important; }
    .embedImage { display:none !important; }
</style><![endif]--><!-- __merge_tags_in_links__ --><style></style><div role="article" aria-roledescription="email" aria-label="email_name" lang="en" style="font-size:1rem"><div style="display:none;max-height:0px;overflow:hidden;"> The more code your tools generate, the more important fast guardrails become. &nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌&nbsp;‌ </div><table role="none" width="100%" border="0" cellspacing="0" align="center" cellpadding="0" class="gg"><tbody><tr><td align="center" valign="top"><table role="none" width="670" border="0" cellspacing="0" cellpadding="0" class="aa" style="width:670px;table-layout:fixed;"><tbody><tr><td class="bodyWrapper" align="center" valign="top" style="padding:10px 5px 10px 5px;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td align="center" valign="top" style="border-width:0px 0px 0px 0px;border-style: solid; border-color: #FFFFFF;border-radius:10px 10px 0px 0px;background-color:#FFFFFF;" class="c"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr id="header"><td style="padding:15px 15px 0px 15px;"><div style="padding-top:0px;padding-right:0px;padding-bottom:20px;padding-left:0px;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td class="f" align="right" valign="top"><p> July 20, 2026 &nbsp; | &nbsp; <a data-read-online-tooltip="true" href="https://link.mail.beehiiv.com/ss/c/u001.RDQHxrvbKYhK2fnxN6upgccvCSylIYgGdTl38H2siOH8vU3cAyECU38zMvds11oXTBGQooOfkNxXDkiJ8jxWgnIcBMMyPPSu5_Yl14h2KIShQPQptt3LPgBDuv6RQH9_Vhm4LRmKiH1bzn3n2zZiUr1Nix92VB7E4g9_0_vNXMHLGwkp3s1n5N-Jz5apZgtA2GvaqtZEqF_tCudHHdVZn30OYoA2dKoX2jsjqNtjQAd0XUCBZ4ZKLwHC9U1EDk9pwd0l3AN3DF1iQcOjdgsW2N7756mK6cfDwdzq24x_mjtWliVV4lR8nCQmjQgVDCkwnoQi0jWhiBpmg5_h5TChy0PG7HXiN5sFP79SBdaWxD-5U-3W-aRYv-ZDK9_-Y9foN1u1d3Wm1nvsnM9GY16fVjpKKtBkeK3E-qM3xlJgx0eLOIZCt_i0W2YQ4qzns3XnuGWseYklAha-xGJM25pcyI5s2RKZb-chvnOwS0V66rOj8bCFv1UyeGLY64SkjpdVBSoQxiK878rgtyyLOnbCShIvq4_VXC7XJw1uLimv58op7t2gvuLfIUNKUP8ZRvuKjucN5fkpqO0CqN0_K4RL8A2-nBeQuvIyttOV_UKWRNsfuy1U1nL80AnLGGS1qZ-i0VLw_Q-F7QJ65fWonFPW0JOH5Op1xp_2bECO8rbdAIVi_tU8vS0s10F7BhSnc7UPFUbzXtoEV9KEkI05hBwnu1gaNIkCF1zNkZKKiugQXrRSMsR_AFukdrBVQ0NisHZ5AmaFgiqHr2D9wxJV53xwSE33uWv93euc6Y4E5g0wSG4-vM6sIIU5tsFE8Vo8C3sh/4sh/7bSrYOulR92ZwKMoC5s32Q/h0/h001.7CwbSk1ycWiZis5P6t7TjAIEs8DGFJCvXZnGDm3Q1rs">Read online</a></p></td></tr><tr><td class="dd" align="center" valign="top" style="padding:15px 0;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td align="center" valign="top"><h1 style="text-align:left;font-family:'Trebuchet MS','Lucida Grande',Tahoma,sans-serif;font-weight:Bold;font-size:32px;color:#2A2A2A;padding:2px 0;line-height:38px;"> One Tip a Week: Stop Fighting Your Git Hooks </h1></td></tr></tbody></table></td></tr><tr><td style="line-height:0;"><div data-open-tracking="true"> <img src="https://link.mail.beehiiv.com/ss/o/u001.MAvgyYoH0G6QJnXDPAJaJQ/4sh/7bSrYOulR92ZwKMoC5s32Q/ho.gif" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;"> </div></td></tr></tbody></table></div></td></tr><tr id="content-blocks"><td class="email-card-body" align="center" valign="top" style="padding-bottom:15px;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> This week's tip of the week is <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.F-6YnpojWB-E8JQzRoLzK7a1Pe9vUNFjMInV-Da4xMSxZ8H2YolHCeWag5MzGfgSj80vRLcFYkfDu16YviUp8nS7zwTP5Z7BKWjhmwVe6sRjhiEvYyckXxJFqQ1omWqoNO5_6jmpE41b_0E54oLY9EBdCBXnhNVLcnVbKxijcDS4Q1vYTL7ss2GLd_wY000iiMdrnaLH_MzJ9WPsV362T9FPrdYDBmSYuFLQz6WMhjFNlI7AeNv7askPHzKShq7d/4sh/7bSrYOulR92ZwKMoC5s32Q/h1/h001.1T8ctl3KVyxgirvLUMAIFweO5OgNi4NpQtcJfSm3Ln8" target="_blank" rel="noopener noreferrer nofollow"><span>hk</span></a>. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> I've always found Git hooks useful, but I think they matter even more now that agents are writing and changing more of our code. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> The more code our tools generate, the more valuable it is to automatically catch formatting issues, lint errors, broken tests, or even leaked secrets before they become commits. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> The problem is that slow Git hooks are easy to resent. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> You make a commit, stare at your terminal while formatting runs, then linting, then tests... and before long <code>git commit --no-verify</code> starts looking very tempting. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> That's where hk comes in. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> hk is a Git hook manager that can run independent checks like formatting, linting, and type checking in parallel when it's safe to do so, while still making sure dependent steps happen in the right order. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> hk supports several installation methods across macOS, Linux, and Windows. Check out the <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.F-6YnpojWB-E8JQzRoLzKyXg5iiMSLghGlY4TecO7YHm_Mk9WW_cKXTt6fzIOhHZukFPh5u9aUq-dPrQRdM6SvYl3E5rH7ddtroNQiUWY7E8FHuSVINf2dyxSWL87g-d4khxBtNUbWThAss7EAkarCIAGgyU2vOceV1YwQ3qndhCZdTavsD14ooIsA4p0JPPDrVhtmDWD-ZpEX60mjU8SBKOQqlJmkqOMVvuXhvDTym2uuo60TCXSxv1ZQrS5_bcVAYnQHTOTdCV47iU16DV5Q/4sh/7bSrYOulR92ZwKMoC5s32Q/h2/h001.Upj5ilvKVwmrlj-OAodjLOQPduYY-hn77GOH7bC5ZY4" target="_blank" rel="noopener noreferrer nofollow"><span>installation docs</span></a> for your platform. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> Once it's installed, add its Git hooks to your repository: </p></td></tr><tr><td style="padding:0px 15px;"><pre style="background:#282828;border-radius:10px;color:#FFFFFF;line-height:1.5;overflow-wrap:break-word;padding:16px;white-space:pre-wrap;word-break:break-word;"><code>hk <span style="color: #ffa657">install</span></code></pre></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> I've spent a lot of time in the JavaScript and TypeScript ecosystems, so I've generally used Git hook tooling distributed as npm packages. But Git hooks are really repository infrastructure. The repository might contain Go, Rust, Python, JavaScript, or all of the above. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> If you're happy with tools like <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.lr_l4rMJ5CdOU96S0h4NeHPUAHmqcKLGBqRVvBlP99B-GjCBtfIC7J49SnwwMF1X23oU8YydzsxO4-_8Vvh9vloqJf-5q9k-7fgFw7iRoZC6TqlIBATCFnjYKYw6aQgsObAXOomlZzsGh_8KBM0VWMaJu0zohQBKs8Nx-CESLpbTrtqgi1LEuULA1l32iIyg-jTzy5rryJXxpRTxeQlzb5UdatlQ_GahKs-73m6iztqOQV8mn_bbbWnFCxBWX5sIbREaBXuEMk2gec2KMQvsyQ/4sh/7bSrYOulR92ZwKMoC5s32Q/h3/h001.uW-5JIcJWDwarQvAgXKA_YmO126ec19iuWVlGELRIVU" target="_blank" rel="noopener noreferrer nofollow"><span>Husky</span></a>, <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.lr_l4rMJ5CdOU96S0h4NeIKXC2ttu3kekW6HuTFlRFeYJxwZMPWL30TPVAHczu67v0vvNHbXSoow7ZLYWaNXnIvR70QV4LmWPNLt9DyGkkTvPAudDVVu5yU82xBEz8u1TU1e7E5dJfnt5lAxGZxlK_aMSeliyz-u-PadW0ZdnslYv6Ij_PfB5Mp9zrANOO9ZwbWufjFMduPKFmVefXBTEhY4zyerRsqu8HUhdqWAQVMI83ahA9eOU69bqcecJeljuZr7TLTaCGy84YNNxSx5TqEZ1iim9t-Ou7XH-pCwTyY/4sh/7bSrYOulR92ZwKMoC5s32Q/h4/h001.6SpzGyBvE7tUBAY7ACcIgtDg_ZPZNqFtLSrvRpQlb1s" target="_blank" rel="noopener noreferrer nofollow"><span>lint-staged</span></a>, or maybe <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.I5tBOwkthblaRnRopJ2YqQ8NNpwrxZNxFIAkIHsbjJRlocRfQK7od31oIVaXHzNo8L-TS5ObzotIQxHcJQ1MKaeqEkAwcuoAT2eYVedF0n2tm87NpoR_LpgLdxduho-95-CFdc-QaSds0u0vJqAWnPtL_BtsWZmt4FZgqr7PGze5r79xqMTTTrcFsSaJIY0ZLetVCG5HCIw79zOuoUgbK2qlNC8rAIlQn_kNuh-IUT2x1vUGcH3qMVDxHPCqsmX7oq5Ov_jtfj8fVuWv5daQY4l73epto-rr4Y4gRE9jtOs/4sh/7bSrYOulR92ZwKMoC5s32Q/h5/h001.9jq2sGL2gsDrsHcFiniJLAfaz1j4oGesjx2VcYAvHLE" target="_blank" rel="noopener noreferrer nofollow"><span>Vite+'s hooks support</span></a>, there's no urgent reason to switch. But if your hooks are starting to feel slow, or you're working across multiple languages, hk is well worth a look. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> Give it a go and let me know what you think. </p></td></tr><tr><td class="dd" align="left" style="padding:0px 15px;text-align:left;word-break:break-word;"><p style="mso-line-height-alt:150.0%;"> That's it! Short and sweet. Until the next one! </p></td></tr></tbody></table></td></tr></tbody></table></td></tr><tr><td align="center" valign="top"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td></td></tr><tr><td class="b" align="center" valign="top" bgcolor="#030712" style="padding:0px 0px 0px 0px;border-style:solid;border-width: 0px 0px 0px 0px;border-color: #FFFFFF;border-bottom-left-radius:10px;border-bottom-right-radius:10px;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td align="center" valign="top" bgcolor="#030712" style="padding:12px"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td><span style="padding-left:1px;"></span></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.LDePdm21VqNWfzzcmxk5ihYmby9w92hVQ5oHNWKCt3uDk-qKATklQVXPAhcTFIFITkXoLNhSDsh2oWWr7fLWuZAccKf4hZV161cID1QTie7POv0bbm5RrtHV46jLYaPRyygcNzNiDsIZr6z967z13Ythk_ZGcpzhXTqvPX9euE-siNV1AV9lvtYhREr_Ma4PhKV27cozgtcrkVj_pfs34lTnmlLlZA6Su7KcvLMlnIs2vdev6l5-u0EDRSamEEHWGZvrExlB5A4RrBa4z82oVA/4sh/7bSrYOulR92ZwKMoC5s32Q/h6/h001.p095CFBi7f_15cIRq9YvQ6mLQcGWTqv7xrVQdgWmjMg" style="text-decoration:none;"><img width="22" height="22" alt="tw" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/x_light.png"></a></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.m8EJf3Sjn2nq8_06WKDhKLHRo1n5W8kfz0RWCnfYYd0ygdykqnVy1dCPGH7rUT8FCfLh9dQpEBxcIFkcLctORyykdrRXz6bsfjp6HrhOyYXUyL01SBUcji3q84ASdVvt0axB1KlugtI5fz0lBt8LZMnH4DHj9m0mPGR0yTb0ARo9o50uTgk8iEW0FozkbpmSC5LFlm8y9LriTr-YXy9ZQQ27la98JvJ7cYE-s2gRtfhT3BNJLtWd6FGRCyP4CwPmdTeiOcHNKcjfLuldK39xNA/4sh/7bSrYOulR92ZwKMoC5s32Q/h7/h001.7ggFzwNFXHxbS_LREPBODFY3pGtdWL3Yci0yxhsMP3c" style="text-decoration:none;"><img width="22" height="22" alt="ig" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/instagram_light.png"></a></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.LDePdm21VqNWfzzcmxk5io7z6xMpo6uYC1uxmeXXKtyEobbKbaXT51ApX3PisDac-TYvMXlFzL66eR1jFOIgBDylwNTmMdAwk59ssfvHFLddzy5EFnICyRybEdtdwAfkQBk0UrAXGctUaqbs0_ney47DAJvYlXstrCIt5r_snQtYeRauAdsTo3tA6eQC0shZ0ixssVGzYGdyF0IMdYiirCNRVDmyVTw850_IIlYGJPdwNJGDVWiOoTZKgoEdvCSkarA2jYZ6MDFXdb-sMvbUMQ/4sh/7bSrYOulR92ZwKMoC5s32Q/h8/h001.PoizSUi023fwGTtjLI02XbZHGgUa5Cfkg4UlEGHqQmI" style="text-decoration:none;"><img width="22" height="22" alt="thds" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/threads_light.png"></a></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.DuJ7J3ipIXrmRBBeGVS35RPBKW9qlLyOMmdV-sEIpiPXFnqejaMnob4OBeiouyZryw9cQoK8j2FiRorhsVyrjdWc7NHgo6ghU-WU6-6oWprQZWqz5TEhZGTTjRZAbAbcJQ4tJxPLQAS5kaxVr1XEG5YCJeMVA1ZYe3HJUAxKQyErYkq5HBZaJ1UHZFXxNH-R0TThFwDSLffcFs5imVNnc5gyAAjRvyQ4BvCmZ5bdqcrxvBqWpHc_qIWrWnfskRFYkVyABpgT8xje13bphxGSw1KG5iShL79Om7nccp8mVso/4sh/7bSrYOulR92ZwKMoC5s32Q/h9/h001.WVlvG3b1O5I-VZmJ2bkfxbDKXJtp8AC5WrFJj3UilvA" style="text-decoration:none;"><img width="22" height="16" alt="yt" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/youtube_light.png"></a></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.DuJ7J3ipIXrmRBBeGVS35TTTJYx3umH6jbsUFMQegvRr_UxIKeNzXLsxH-1vncWSxtwJti7hYFLzeRnNNXcSQ4PmjtGPGHqtJjp2_sKaSwQg5R-Y1eeivB72VJPr5AArOZTTisM5PUD1GqgLsG2sC0Q5T_PjYaXb8_Fi_wLx20fbcOC6vDsTdE1pJ3Kuwuuruaz6QGMQ8kVb2AMe1v8mroCNGVrU3WF6dM-r_9LOdeTzhvWhRHV5dU_kwgNWazNQq1LSpl2rT8TtW7cZ3EbhFWiS1QsE6QWOMHmCVu6gUlk/4sh/7bSrYOulR92ZwKMoC5s32Q/h10/h001.Y-Z2-mzLpO5ZJVFqEHHz_82PUPLiupYyBwmzBVdpUS0" style="text-decoration:none;"><img width="22" height="22" alt="tk" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/tiktok_light.png"></a></td><td align="center" valign="middle" width="75" style="width:75px;"><a href="https://link.mail.beehiiv.com/ss/c/u001.DuJ7J3ipIXrmRBBeGVS35YgB1KY3ni3uh-SuIwrhLbsY9gWkkqnJkhWg-DoVHPWyuUecCRPB7_5J_ppbjSsjREkO3i_yhnktXDIyhPp58dJdj1IReBeNFnFK8rVMDRZjPKnFl6EEej5tg_nPmZZfZpoPLdh1q7OC5rxEgnwOSqVJG0aylsCLOg26LHe2w_qGt5ZgE4AHU-B8goB_yZa6YHgVDf9zKXfn9Kxkoo23B-kU3Yr12oUGjB_bSLnzcCMhJoauyVQPnC-2YUwJ57jWUDI0o8aAGgdZQ5TSJE040q0/4sh/7bSrYOulR92ZwKMoC5s32Q/h11/h001.nh7sQTi_6d9VAsUvSR89IZRkut3-9o92zMr3CDuGZDg" style="text-decoration:none;"><img width="22" height="22" alt="in" border="0" style="display:block;max-width:22px;color:light" src="https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,format=auto,onerror=redirect,quality=80/static_assets/linkedin_light.png"></a></td><td><span style="padding-left:1px;"></span></td></tr></tbody></table></td></tr><tr><td height="10" style="line-height:1px;font-size:1px;height:10px;"> &nbsp; </td></tr><tr><td class="w" align="center" valign="top" style="padding:15px 15px 15px 15px;"><table role="none" width="100%" border="0" cellspacing="0" cellpadding="0" align="center"><tbody><tr><td align="center" valign="top"><p style="font-family:'Verdana',Geneva,sans-serif;color:#FFFFFF!important;"> Update your email preferences or unsubscribe <a class="link" href="https://link.mail.beehiiv.com/ss/c/u001.RDQHxrvbKYhK2fnxN6upgccvCSylIYgGdTl38H2siOELVntw12ohnOa59bsHFuFl2AlORpxvLEo7BQq0aVT_qf9KpZ1T3tndOMIjueh0WS7qmHIhgu67WkYG8pxNjtsH5M-_S1UTZtNlEcAfMyDBVY5xnF09tmFUWI_LWJBBw-LBfcrFovyTC94r0niUnVZp8xuIBzvUlo0pHIXgRtNdsl_zFL_1F2QoIWCQxsrCf_KFyq78fWOv0M3PlSK07T_q88NRA0xXhzonZ-kYSFX03ubpuB-xYJ4YiqJz61Sio-2JDXo-SbDAH6qC9on1XMV5OOfbxJhrLsTtWLWawADQ6vdXSp6zM_rvyusHQ96E5KHLlu9Pje_dbhohKnzqkD7iHYAhnwkIIy0PYHIc5xVRRu8vWrv6iOjrIyTAXLYfaqz_VcNTdy2EZd_EjuUeyvBxhhwzFTvqrJAkgdY6Q0uVp2G8RPBr6URumXJRM5sD2zQQgoolOlOVKuX6UB61epmgl0gOhHTMzuYoxoAB6r6LrMIOhOXLxh8vAVnJ3JixiVhQzwxcfvqtNnhP49_t6dn9Iali0tZzyM0_0EtLa_819cDOPDz27sPk0W3g5oZoY--UmPRjZ-hbEHNMfxhkmsRapf5YrB1zCnPxv2Tpnhn59OKv09LlHWmv_Wi2NGN7iZF188pMkzMxx_7Y6jm8_FofILn-gGq6fjpFCQJJe1Bm6I_J6e0HAra3XAOlKokShx3ZBkwhD7sUk4gCthbnar6rvCM5UJmvRLnD_HqGSS6ajVSNXGhD1W_rJIXqKOkRUA3kqsnB5_ATHihURYfr1z8rTaZud3zAaSmVxcPVAll1X41fZlXN_FmBQwU5In_n3Mwmiy0eniM621qRdjdqmkTlV_zowauKgWFLsB1dgoUqmippuIY93LcAXhnLXMk_SK_CG37lKVVJTO6xtUjPM41PrRp1Ani268qP4nHDKZLRIT6L84znQo6UR4dKqsjAdqsA1zaSH46RzlmkhP5mhuqQyMlnm-NslyL5YQzKxuDcyZtMJvY9s_gVxj-WIouc0WwjmbY3XIXDqqNB9qGqne6u/4sh/7bSrYOulR92ZwKMoC5s32Q/h12/h001.KDuJ6ZDJHfyoqNc17-t4gZB55HWXmMRTi_UziWcYpxs" style="text-decoration:underline;text-decoration-color:#FFFFFF!important;color:#FFFFFF!important;"> here</a></p><p class="copyright" style="font-family:'Verdana',Geneva,sans-serif;color:#FFFFFF!important;"> © 2026 One Tip a Week </p><p style="font-family:'Verdana',Geneva,sans-serif;color:#FFFFFF!important;"> 228 Park Ave S, #29976, New York, New York 10003, United States </p></td></tr><tr style="display: table-row !important;"><td align="center" valign="top" style="padding-top:20px;"><table role="none" border="0" cellspacing="0" cellpadding="0" align="center" style="display:table !important;"><tbody><tr style="display:table-row !important;"><td class="u" align="center" valign="middle" height="32" style="height:32px;display:table-cell !important; max-height: 32px !important;margin:0px !important; background-color: #ffffff !important;"><a style="line-height:32px !important;text-decoration:none;display:block !important;" href="https://link.mail.beehiiv.com/ss/c/u001.DuJ7J3ipIXrmRBBeGVS35R87KxItOKRbBz5vf_vz8VR99kvcC7lPpEris41Wo0-NscbgwXB3KvUMsgJT5qJoP7Ccx8Xt_xfA1iiXeB6036TmAysXHq1FZssLcBK59DKnkCl2HlpzLHaL5eOUkbzp__--JUI9VH7pHM2jgs6QlwsnEfBQyFFYrr6cBJT55590GKGMBLcSJj_aVmIxrYlLHASL8JZZTqsO3BJ_c_xiycHwLl7i1llfydFupZO3humT/4sh/7bSrYOulR92ZwKMoC5s32Q/h13/h001.RaFI4HmqxrPqrfrERUIqE6aB2bRQ2Qs6UOSQsi5IubM"><img src="https://media.beehiiv.com/output-onlinepngtools.png" width="16" alt="beehiiv logo" style="display:inline-block !important;max-width:16px !important; vertical-align:-3px !important;width: 16px !important;" border="0"><span style="padding-left:11px !important;display: inline-block !important;">Powered by beehiiv</span></a></td></tr></tbody></table></td></tr><tr><td align="left" valign="top" height="2" style="height:2px;line-height:1px;font-size:1px;mso-line-height-rule:exactly;"><a href="https://link.mail.beehiiv.com/ss/c/u001.F-6YnpojWB-E8JQzRoLzK4aWwDajGk0mCc_8VzdEgKT7CY0Z6A3JiUGCeks1tFHSdiR9s0smDhWKIbAcu_4stPYd5EqfOF_7m8ucnuSHuqs/4sh/7bSrYOulR92ZwKMoC5s32Q/h14/h001.y6d7N6TMTNe1ekFJCQNbbPVxa13Fneejd2fXopm9Qjc" style="color: #030712 !important; cursor: default; font-size: 1px; text-decoration: none;">Terms of Service</a></td></tr><tr><td align="left" valign="top" height="2" style="height:2px;"><a href="https://email.beehiivstatus.com/619dd9eb76a0b8d5e45ae0be9f5e1d2271ab2a11/hclick" clicktracking="off" style="color: #030712 !important; cursor: default; font-size: 1px; text-decoration: none;"></a></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table></div>]]></description>
      <pubDate>Mon, 20 Jul 2026 11:00:53 +0000</pubDate>
      <link>https://newsletters.feedbinusercontent.com/ea3/ea34fa227190eb08a9ffe0f1379437759a88e3ad.html</link>
      <dc:creator>Nick Taylor</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5314224164</guid>
    </item>
    <item>
      <title><![CDATA[Do we still need build tools?]]></title>
      <description><![CDATA[<p>How necessary are build tools for frontend development in 2026? Let’s take a look.</p>
<br><br>]]></description>
      <pubDate>Mon, 20 Jul 2026 07:43:36 +0000</pubDate>
      <link>https://olliewilliams.xyz/blog/no-build/</link>
      <dc:creator>Sidebar</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5314023368</guid>
    </item>
    <item>
      <title><![CDATA[When It Makes Sense To “Block” The Main Thread]]></title>
      <description><![CDATA[<p>We’ve all heard of the sacred rule in modern web development, the rule never to be broken. The rule of <em>“Never block the main thread.”</em></p>
<p>You almost can’t miss it as a web developer; it’s in almost every performance guide, and to be fair, it is good advice. We all know the browser’s main thread is <strong>single-threaded</strong>, meaning it can only do one thing at a time.</p>
<p>Plus, as we know, <a href="https://www.smashingmagazine.com/2023/10/speedcurve-fight-main-thread/">the main thread isn’t ours alone</a>; we share it with the browser’s rendering engine, input handlers, and other critical tasks. As a result, the less time we hold onto the main thread, the more responsive an app feels. That leads us to share tasks with background workers as we’ve convinced ourselves there should be a hard line between the UI and any computation, and that line shouldn’t be crossed.</p>
<p>And that is what a “recommended” architecture looks like.</p>
<p>But I dare say that <em>sometimes**,</em> moving the data to a worker is slower than just letting the main thread do the work.</p>
<p>I found this out a few months ago while building a Chrome extension with screenshotting features called Fastary. I kept finding a latency of about 2 to 3 seconds in all my testing, even after using an <a href="https://developer.chrome.com/docs/extensions/reference/api/offscreen#main-content">Offscreen Document</a> (a background process in Chrome extensions) to handle the canvas operations. A screenshot task should feel instant without lag, after all.</p>
<p>It is quite ironic that by reflex, we move work away from the main thread to avoid freezing the UI, but sometimes the act of moving that work (e.g., serializing, copying, and deserializing) can also freeze the UI. And <em>sometimes</em> the recommended approach of letting the background do the work can be slower than just doing the work on the main thread.</p>
<p>Let’s talk about that.</p>
The Architecture Of Browser Context Isolation
<p>To put things in perspective, let’s understand why we isolate browser contexts and how they communicate with each other, with emphasis on the <em>communication</em> part.</p>
<p>A browser is more than a single environment. Different environments are running at the same time, each having its own memory space, what it can access, and rules:</p>
<ul>
<li>The <strong>main thread</strong> is what we are most familiar with; this is where JavaScript logic runs, where the DOM lives, where styles get rendered, and where users interact.</li>
<li>The <strong>Web Workers</strong> are separate threads that can also execute JavaScript without DOM access. We mostly use this for heavy data tasks.</li>
<li>The <strong>Service Workers</strong> are network-related proxies in charge of intercepting network requests and can even run when the page is closed.</li>
<li>And then there are <strong>Chrome extension contexts</strong>, where we have background service workers, content scripts, and Offscreen Documents (the relevant ones for this article).</li>
</ul>
<p>Each one of these is isolated from the others. A web worker or background script lives in a different memory space from the main thread. They cannot just reach and read each other’s variables or logic, and this is known as the <strong>“shared-nothing” architecture</strong>.</p>
<p>How do these isolated environments communicate? They explicitly message each other back and forth using APIs, like <code>postMessage()</code>.</p>
<h3>The Structured Clone Algorithm</h3>
<p><code>postMessage()</code> tells the browser to take a piece of data and deliver it to the context that requested it. But to do this, the browser relies on the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm">Structured Clone Algorithm (SCA)</a>.</p>
<p>You’re probably familiar with <code>JSON.stringify()</code>. SCA is similar, but much stronger and smarter. In its simplest form, SCA is a deep, recursive copy operation, i.e., cloning. It walks through the entire data structure it is given, clones every single value, serializes it into a transportable format, ships those bytes to the target contexts, and then reconstructs the original object on the receiving side.</p>
<p>SCA is fast, or maybe fast-<em>ish</em>... For a small regular config object like <code>{theme: "dark"}</code>, it is imperceptible; you don’t even notice it. The story changes, however, when dealing with heavy data because the SCA is a synchronous blocking <code>O(n)</code> operation, i.e., the cost increases linearly with the size of your data.</p>
<p>Let’s put that into perspective. A user clicks a button, and internally, an 8MB image payload is sent to a background worker for processing. When you call <code>postMessage()</code>, the main thread must immediately stop what it is doing to run this serialization and copying process.</p>
<blockquote>So, if the time it takes to pack, ship, unpack the data, and go back to the start is longer than the time to just process the data on the main thread, why not do that instead?</blockquote>

What About Transferable Objects?
<p>I’m sure some of you are already thinking, <em>“Why not just use Transferable objects?”</em> And that is a valid point. Let’s talk about that.</p>
<p>Developers who really pursue ultra-high-performance web apps usually use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects">Transferable objects</a> (e.g., <code>ArrayBuffer</code>, <code>ImageBitmap</code>, or <code>MessagePort</code>) to bypass the Structured Clone Algorithm. This is because when you transfer an object, you’re not making a copy (like SCM). Instead, the browser switches ownership of the data from one context to another.</p>
<p>The browser performs a hand-off whereby the sending context loses access to the data instantly, and the receiving context takes full control. It is actually insanely fast. According to <a href="https://developer.chrome.com/blog/transferable-objects-lightning-fast#benchmark_demo">Chrome Developers’ benchmark</a>, transferring a massive 32MB <code>ArrayBuffer</code> can take under 7ms, compared to about 300ms when cloning with SCM. That’s a 43x speed boost.</p>
<p><img src="https://files.smashing.media/articles/when-makes-sense-block-main-thread/1-structured-cloning-transferable-objects.png"></p>
<p>But like all good things, there are downsides. To name a few:</p>
<ul>
<li><strong>You lose it once you send it.</strong><br>If the UI still needs that data (like to show an image preview), you can’t access it anymore.</li>
<li><strong>Not all data is transferable.</strong><br>A plain JS object is not. A Blob is not. Even a Base64 string is not.</li>
<li><strong>API limitations.</strong><br>In the context of browser extensions, Chrome’s internal messaging (<code>chrome.runtime.sendMessage</code>) traditionally forces everything through JSON serialization.</li>
</ul>
<p>So, as far as my screenshot extension went, Transferable objects were not an option.</p>
Why We Isolate Contexts Anyway
<p>Why do we even bother isolating contexts at all? Why not just leave it all to the main thread?</p>
<p>Offloading long-running CPU tasks to a background thread is absolutely the right thing to do. The browser needs to paint a new frame <strong>every 16.6ms</strong> to keep things fluid; that means <a href="https://developer.mozilla.org/en-US/docs/Glossary/Long_task#:~:text=A%20long%20task%20is%20a%20task%20that%20takes%20more%20than%2050ms%20to%20complete.">any task that takes &gt;50ms is generally considered “long”</a>. Offloading to the background is absolutely the right thing to do.</p>
<p>The issue, however, is that we’ve turned this <em>“never block the main thread”</em> into an absolute rule, without asking <em>is this task expensive to process or expensive to move?</em></p>
<p>I have come to realize now that the rule is less “never block the main thread” than “never block the main thread for too long.”</p>
When The Right Architecture Is The Wrong Architecture
<p>My goal with the Fastary extension was to make it feel like a native app, running as smoothly and instantly as you would expect a native app to.</p>
<p>As you already know, I took <a href="https://developer.chrome.google.cn/blog/Offscreen-Documents-in-Manifest-v3">the recommended approach</a> to use the Offscreen Document to handle DOM work in the background. But to my surprise, that took a different turn.</p>
<p>The Offscreen Document API is a clear winner. You create a hidden, undisplayed document that runs entirely in the background. It has a DOM and supports Canvas. For example, if I want to crop a screenshot, stitch multiple screenshots together, perform heavy image manipulation, or add a watermark, Offscreen Document was made for that.</p>
<p>Turns out that was not the best approach. This was my architecture:</p>
<ol>
<li>The background Service Worker captures a screenshot with <code>chrome.tabs.captureVisibleTab()</code>, which returns a Base64-encoded data URL string.</li>
<li>The background Service Worker uses <code>chrome.runtime.sendMessage()</code> to ship this image payload to the Offscreen Document.</li>
<li>The Offscreen Document receives the image, loads it into an <code>&lt;img&gt;</code> element, then draws it onto a canvas before it applies the user’s crop coordinates, encodes the result, and sends the processed image back to the background worker.</li>
</ol>
<p>But when I tested it, the screenshot didn’t feel instant. As I said earlier, there was a consistent 2–3 second lag.</p>
<p>I figured out that when <code>captureVisibleTab()</code> takes a screenshot, it returns a Base64 URL string, and on a standard 1080p screen, that string could be approximately 1MB or more, depending on how detailed the image is. It gets even more interesting on modern Retina displays (e.g., MacBooks) as they tend to automatically double the image’s size by default.</p>
<p>Keep in mind that since the image payload could be doubled and extension messaging relies on JSON serialization (as of this writing), we potentially deal with <strong>massive synchronous communication</strong> that costs an entire round trip.</p>
<p>The image string data is JSON-serialized at least twice: once when going into the Offscreen Document and once coming back out with the processed results to the background worker. The actual image processing (cropping) done inside the Offscreen Document was fast, no doubt, but I can’t say the same about the transfer overhead.</p>
<h3>The Retina High-DPI Problem</h3>
<p>As if the latency itself wasn’t enough, I noticed a rather subtle bug — which, now that I think of it, was more of my ignorance. After a screenshot was taken, the crop result was completely off in a way that either weirdly scaled the image or resulted in incorrect coordinates.</p>
<p>It turns out that when a user selects a region to crop, the content script gets the box coordinates using <code>getBoundingClientRect()</code>, which is measured in <strong>CSS pixels</strong>; this is what the DOM uses. But when the screenshot is captured natively in Chrome, the browser doesn’t crop it automatically; it instead uses the <strong>physical hardware pixels</strong> to get the full screen capture. And the browser uses <code>devicePixelRatio</code> (DPR) to know how many physical pixels should represent one CSS pixel. Basically, if a user on a Retinal display (DPR = 2) highlights an area of 400x300 CSS pixels, the actual captured image area is 800x600 physical pixels.</p>
<blockquote><strong>Note:</strong> One CSS pixel is equal to 1 physical pixel (DPR of 1) on a standard monitor. On a Mac Retina display or a modern 4K monitor, however, the DPR is usually 2 or 3.</blockquote>

<p>For an accurate crop, I needed to apply these two different measurement systems with the right DPR, i.e., scale the crop coordinates by the DPR. But remember, Offscreen Documents have no physical display. Processing any image would have a default DPR equal to 1. To fix this, I would have to capture the exact <code>devicePixelRatio</code> from the active tab, serialize it, pass it alongside the image payload, and manually do the scaling math inside the Offscreen Document. The complexity starts to compound.</p>
<p><em>What if I broke the golden rule and did the work on the main thread instead?</em></p>
Working On The Main Thread
<p>Some developers will argue that UI tasks are the only things that should run on the main thread, but I don’t fully agree with that. Personally, I believe that user explicitly-invoked actions that need immediate results can sometimes get a solid pass to run on the main thread, provided the work is incredibly <em>fast</em> (e.g., 1s).</p>
<p>That’s what I did: scrap out the Offscreen Document and reengineer the logic. Instead of:</p>
<div>
<pre><code>Background → [serialize] → Offscreen Document → [serialize] → Background → Content Script</code></pre>
</div>

<p>…I decided to run the whole image processing in the active tab:</p>
<ol>
<li>The background Service Worker captures the screen and gets the Base64 string (same as before).</li>
<li>The background sends the payload directly to the content script in the active tab using <code>chrome.scripting.executeScript()</code>.</li>
<li>The content script (running on the main thread) receives the payload, draws it to a canvas, performs the crop using the correct DPR value, and copies the result to the clipboard.</li>
</ol>
<div>
<pre><code>// Background Script
const screenshotUrl = await chrome.tabs.captureVisibleTab(undefined, { format: "png" });

// Inject the processing function into the active tab as a content script
await chrome.scripting.executeScript({
  target: { tabId: activeTab.id },
  func: processAndCopyImage,
  args: [{ base64Image: screenshotUrl, cropData: userSelection }]
});
</code></pre>
</div>

<p>This approach completely clears out multiple context hops and round trips that JSON serialization requires. The only cross-context transfer involves sending the data URL from the background to the content script.</p>
<p>The Retina DPI issue essentially solved itself, as the content script runs directly inside the real, active browser tab because it knows the monitor’s real <code>devicePixelRatio</code>.</p>
<p>But there’s an elephant in the room that you may have noticed.</p>
<p>Sure, the image is now processed on the main thread, and the background manipulates the canvas in the active tab. I could technically be blocking the main thread. That’s where I amended the “no blocking the main thread” rule to “no blocking the main thread <em>for too long</em>.” In this specific case, at least, blocking the main thread for a task the user requests for approximately one second is justifiable. It works conversely as well: maybe <strong>don’t isolate processes if the data transfer cost is greater than the processing cost</strong>.</p>
Conclusion: When To Isolate And When Not To
<p>I’ve boiled it down to a mental model that depends on whether the task is:</p>
<h3>1. Compute-Heavy Tasks (CPU-Bound)</h3>
<p>These are tasks where the primary cost is computation and not the size of the data itself. These are tasks where most of the time is spent on doing calculations or heavy transformations, e.g., image compression, audio profiling, physics simulation, etc.</p>
<p>The transfer cost for these tasks is minuscule compared to the actual work.</p>
<h3>2. Data-Heavy Tasks (Data-Bound)</h3>
<p>These tasks are the exact opposite. These tasks are only expensive because of the size. The processing time is almost insignificant, but the data is expensive to transport, e.g., image cropping, filtering an array, shallow copy, etc.</p>
<p>In my specific case, offloading the task to the background falls mostly into <strong>negative-sum efficiency</strong>. If we are talking about moving megabytes of data to perform a 50ms operation, there is no benefit to offloading it to the background.</p>
<p>Perhaps we can think of it like this:</p>
<pre><code>Total Time = Serialization Cost
  + Transit
  + Background Processing Time
  + Deserialization Cost
</code></pre>

<p>Looking at this, if the <strong>“background processing time”</strong> is the most dominant task in your operation, then isolation is the clear winner. But if serialization, plus deserialization, plus transit exceeds that cost, then there’s no need to isolate things.</p>
<p>And if you can’t figure out if the task is CPU-heavy or data-heavy, it certainly doesn’t hurt to measure it, for example, using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark"><code>performance.mark()</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceMeasure"><code>performance.measure()</code></a> around <code>postMessage</code> calls to profile the transfer cost.</p>
]]></description>
      <pubDate>Fri, 17 Jul 2026 08:00:00 +0000</pubDate>
      <link>https://smashingmagazine.com/2026/07/when-makes-sense-block-main-thread/</link>
      <dc:creator>Articles on Smashing Magazine — For Web Designers And Developers</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5310277695</guid>
    </item>
    <item>
      <title><![CDATA[Flodesk Studio: AI email design, built by real designers]]></title>
      <description><![CDATA[<p>Describe what you want, get a beautiful on-brand email. Human-crafted foundations, AI-accelerated. Free in beta, works with any ESP.</p>
<br><br>]]></description>
      <pubDate>Mon, 13 Jul 2026 07:47:00 +0000</pubDate>
      <link>https://bit.ly/side-flo1</link>
      <dc:creator>Sidebar</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5304592863</guid>
    </item>
    <item>
      <title><![CDATA[Web Weekly #196 (#blogPost)]]></title>
      <description><![CDATA[
        
          <div class="markdown"><div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="guten-tag!-guten-tag!-%F0%9F%91%8B" href="https://www.stefanjudis.com/blog/web-weekly-196/#guten-tag!-guten-tag!-%F0%9F%91%8B">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="guten-tag!-guten-tag!-%F0%9F%91%8B">Guten Tag! Guten Tag! 👋</h2></div><p>Do you know what shipped in the new JavaScript version? Do you know about all the fancy anchor positioning features yet? And do you use CSS <code>zoom</code>?</p>
<p>Turn on the Web Weekly tune and find some answers below. Enjoy!</p>
<p>::: song by="Olivier" title="Connor Price &amp; Nic D - Too Easy" youtube="zOVkU-P3SyA"
It's been on repeat in my playlist for the past two weeks since I watched an Android Gemini Intelligence ad... :D
:::</p>
<p>Do you want to share your favorite song with the Web Weekly community? <a href="mailto:stefan@webweekly.email">Hit reply</a>; there are <strong>five more songs</strong> left in the queue.</p>
<hr aria-hidden="true"><p>If you enjoy Web Weekly, share it with your friends and family.</p>
<ul>
<li>🦋 <a href="https://bsky.app/profile/stefanjudis.com/post/3mqjebggbbs2j">Bluesky</a></li>
<li>🐘 <a href="https://front-end.social/@stefan/116911917515545466" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Ffront-end.social%2F%40stefan%2F116911917515545466')">Mastodon</a></li>
<li>💼 <a href="https://lnkd.in/p/dfrzGi_y" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Flnkd.in%2Fp%2FdfrzGi_y')">LinkedIn</a></li>
</ul>
<p>A quick "repost" really helps this indie newsletter out. Thank you! ❤️</p>
<hr aria-hidden="true"><div class="highlightBox sponsored margin-top-xl margin-bottom-xl">
      
          <div class="cornerBubble">
            <svg aria-hidden="true">
              <use xlink:href="/sprite.svg#icon-sponsored"></use>
            </svg>
          </div>
        

      <div class="highlightBox__pill">Sponsored</div>
      <div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="full-featured-react-event-calendar" href="https://www.stefanjudis.com/blog/web-weekly-196/#full-featured-react-event-calendar">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="full-featured-react-event-calendar">Full-Featured React Event Calendar</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(10,76,212); --color2: rgb(96,122,142); --color3: rgb(251,212,132); --color4: rgb(185,193,201); --color5: rgb(43,59,76);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTUwIj48cGF0aCBmaWxsPSIjMmIzYjRjIiBkPSJNMCAwaDMwMHYxNTBIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgcj0iMSIgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJtYXRyaXgoLTg0LjM2NjA4IDcuMjMyNzIgLTE1LjAxNjk2IC0xNzUuMTY1MyAxOTkuNSA4NikiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0ibWF0cml4KDYuNzA1OTMgLTE1My41OTEwNyA3Mi4zNTE5NSAzLjE1ODk1IDE4OCA4NC4zKSIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik03IDQ4aDYzdjIwSDd6Ii8+PGVsbGlwc2UgY3g9IjE4MiIgY3k9IjEiIGZpbGw9IiMwMDAwODIiIHJ4PSI5NyIgcnk9IjYiLz48ZWxsaXBzZSBjeD0iMzgiIGN5PSIxMDUiIHJ4PSI3OCIgcnk9IjM5Ii8+PGNpcmNsZSBjeD0iMTkwIiBjeT0iNzkiIHI9Ijc0IiBmaWxsPSIjZmZmIi8+PGVsbGlwc2UgY3g9IjQyIiBjeT0iMTMiIHJ4PSI3MyIgcnk9IjE5Ii8+PGVsbGlwc2UgY3g9IjEwNSIgY3k9Ijc1IiBmaWxsPSIjMDAwMDQxIiByeD0iMTEiIHJ5PSIxMjciLz48L2c+PC9zdmc+');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="500" srcset="https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/56hu5SlTE3bN7qoZGYbn3y/dc796478792e31dbce7e88236ae4370e/calendar_webweekly.png" alt="Modern React Calendar — free, full-featured event calendar with TypeScript support &amp; MIT license." loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>SVAR React Calendar brings Google Calendar-style scheduling to your React app — multiple views, drag-and-drop event editing, and a built-in edit form for creating and updating events. Events are easily organized into color-coded calendar groups.</p>
<p>It's built natively in React, with no wrappers or adapters, and includes TypeScript support, easy backend integration, light/dark themes, free core + PRO with timeline, agenda, and resources views.</p>
<p>Start building scheduling UIs in minutes.</p>
<p><a class="btn btn__small" href="https://svar.dev/react/calendar/?utm_source=webweekly&amp;utm_medium=newsletter">Try SVAR React Calendar</a></p>
<p><small>Web Weekly is open for sponsorships. When you want to reach 6k developers, <a href="https://webweekly.email/advertise">you know what to do</a>!</small></p>
</div><div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="web-weekly-housekeeping" href="https://www.stefanjudis.com/blog/web-weekly-196/#web-weekly-housekeeping">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="web-weekly-housekeeping">Web Weekly Housekeeping</h2></div><p>This week's bag of karma points goes to <strong>Chris, Spike, Dan, James, and Gerrit</strong>, who all started to support Web Weekly financially. This brings the supporter count to mind-boggling 53. After subtracting the cost of sending and hosting, Web Weekly makes now a bit under $150 per month.</p>
<p>Thank you all for supporting indie publishing. ❤️</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="something-that-made-me-smile-this-week" href="https://www.stefanjudis.com/blog/web-weekly-196/#something-that-made-me-smile-this-week">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="something-that-made-me-smile-this-week">Something that made me smile this week</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(210,133,65); --color2: rgb(132,124,124); --color3: rgb(229.60851063829787,186.1608510638298,147.79148936170213); --color4: rgb(213,207,200); --color5: rgb(84,60,43);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTM3Ij48cGF0aCBmaWxsPSIjNTQzYzJiIiBkPSJNMCAwaDMwMHYxMzdIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjExMSIgY3k9IjQwIiByPSIxNjYiLz48cGF0aCBmaWxsPSIjYmVjNmM3IiBkPSJNMzQuMyA2MS41IDIwLjYgNDUuNmwxNS0xMyAxMy44IDE1Ljh6Ii8+PGNpcmNsZSByPSIxIiBmaWxsPSIjYjJiYmJjIiB0cmFuc2Zvcm09InJvdGF0ZSgtOTEuNiAxMjkuMyAtODApIHNjYWxlKDExLjE0NjU2IDEyLjI3NikiLz48Y2lyY2xlIGN4PSI4NSIgY3k9Ijk1IiByPSIxMjUiIGZpbGw9IiMwMDA5MTAiLz48cGF0aCBmaWxsPSIjYTA5OTkxIiBkPSJtMTkuOCA0Ny4xIDE2LjctMTMuNyAxMCAxNC41LTExLjggMjB6Ii8+PGNpcmNsZSByPSIxIiBmaWxsPSIjMDAwMTA5IiB0cmFuc2Zvcm09Im1hdHJpeCgzNC45Mjc5MiA5LjMzNTY0IC00Ny4yOTUxNCAxNzYuOTQ3NzUgMjU1IDY5LjEpIi8+PGNpcmNsZSByPSIxIiBmaWxsPSIjMDAwNzBiIiB0cmFuc2Zvcm09Im1hdHJpeCgtNzIuMDE2MTYgLTQwLjU3OTAyIDExLjEzNTg0IC0xOS43NjI5NCAyMDcgNy41KSIvPjxlbGxpcHNlIGN4PSIxNDkiIGN5PSIzNCIgZmlsbD0iI2I3YjRhYSIgcng9IjE0IiByeT0iMiIvPjwvZz48L3N2Zz4=');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="460" srcset="https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/iIUoP7SptJrm37YQ76NB0/e106148b25ef781cd58acf2dd9585926/Screenshot_2026-07-12_at_18.04.24.png" alt="A dark cartoon-like graphic showing two avatars. One avatar says &quot;you there?&quot;" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>Aerospace engineer Cauê added a town square to his site (scroll to the bottom) and this brings back strong vibes of when the internet used to be fun. The project is open source if you want to have a place on your site for people to hang out.</p>
<p><a class="btn btn__small" href="https://cauenapier.com/blog/townsquare_release/">Talk to strangers</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="no-code" href="https://www.stefanjudis.com/blog/web-weekly-196/#no-code">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="no-code">No code</h2></div><ul>
<li><a href="https://pradyuprasad.com/writings/how-to-ask-for-help/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fpradyuprasad.com%2Fwritings%2Fhow-to-ask-for-help%2F')">How to ask for help from people who don't know you</a> → "One of the strongest ways to show that you’re worth helping is to demonstrate that you are a serious person."</li>
<li><a href="https://whitep4nth3r.com/blog/goodbye-forever-probably/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fwhitep4nth3r.com%2Fblog%2Fgoodbye-forever-probably%2F')">Goodbye, forever, probably.</a> → "People aren’t seeking information in the ways we once knew; The Internet and its communities have fragmented."</li>
<li><a href="https://zeldman.com/2026/07/06/memories-cant-wait-or-how-i-learned-to-keep-worrying-about-the-web/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fzeldman.com%2F2026%2F07%2F06%2Fmemories-cant-wait-or-how-i-learned-to-keep-worrying-about-the-web%2F')">Memories Can’t Wait—or, How I Learned to Keep Worrying About the Web</a> → "We need to stop pretending that “the cloud” is a place. It’s not a place. It’s a promise"</li>
</ul>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="anchor-positioning" href="https://www.stefanjudis.com/blog/web-weekly-196/#anchor-positioning">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="anchor-positioning">Anchor positioning</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(240,113,151); --color2: rgb(168,108,115); --color3: rgb(250,186,116); --color4: rgb(164,172,188); --color5: rgb(42,52,76);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTk5Ij48cGF0aCBmaWxsPSIjMmEzNDRjIiBkPSJNMCAwaDMwMHYxOTlIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjEzMCIgY3k9IjQ1IiByPSIxODYiIGZpbGw9IiMwMDA1MTUiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiMyOTJiNDIiIHRyYW5zZm9ybT0icm90YXRlKDEwNC42IDE5IDc1LjQpIHNjYWxlKDM3LjM1NiA2Ny43MzM4NikiLz48cGF0aCBmaWxsPSIjNmU1MzVhIiBkPSJNNDAgNjVoMTc3djVINDB6Ii8+PHBhdGggZmlsbD0iIzg5NWY2NyIgZD0iTTU3IDEwMWgxMjh2NEg1N3oiLz48ZWxsaXBzZSBjeD0iODkiIGN5PSI5MSIgZmlsbD0iIzgxNDM0ZCIgcng9IjQzIiByeT0iMyIvPjxwYXRoIGZpbGw9IiM5ZTRlODQiIGQ9Ik00NiA1M2g1MXY0SDQ2eiIvPjxlbGxpcHNlIGN4PSIxNSIgY3k9IjU3IiBmaWxsPSIjMDgxNzI5IiByeD0iMzIiIHJ5PSIxNjkiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiMwYTFhMmUiIHRyYW5zZm9ybT0ibWF0cml4KDkuODUwNiAtMS4zNDkzNyAxMy4zODQ2IDk3LjcwOTkyIDExOC4zIDExNy4zKSIvPjwvZz48L3N2Zz4=');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="666" srcset="https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/7dxin6dbBf1zhnUIOO61aB/bb042c8ff342f26c04a15dc9b684ef34/code-shot-anchor-scope.png" alt=".card {   anchor-name: --card;   anchor-scope: --card, --card-actions;    .secondary-actions {     anchor-name: --card-actions;   } }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>I'm still wondering about anchor positioning (baseline newly available) use cases beyond the tooltip example. <a href="https://thatemil.com/blog/clickable-surface-expansion-using-anchor-position/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fthatemil.com%2Fblog%2Fclickable-surface-expansion-using-anchor-position%2F')">Emil explains how multiple anchors help out with creating clickable card overlays</a>. Fancy stuff!</p>
<p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(97,228,244); --color2: rgb(169,93,142); --color3: rgb(251,186,116); --color4: rgb(164,172,188); --color5: rgb(45,67,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTY2Ij48cGF0aCBmaWxsPSIjMmQ0MzVjIiBkPSJNMCAwaDMwMHYxNjZIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjExMyIgY3k9IjM2IiByPSIxODQiIGZpbGw9IiMwMDAwMDYiLz48cGF0aCBmaWxsPSIjMzUzNDUyIiBkPSJtNDYuNiA0NS43IDEzMi0yLjMuOCA0NS0xMzIgMi4yeiIvPjxlbGxpcHNlIGN4PSIxMDQiIGN5PSIxMTYiIGZpbGw9IiMwNjEyMjkiIHJ4PSIyNTUiIHJ5PSIzMSIvPjxjaXJjbGUgcj0iMSIgZmlsbD0iIzA1MGUyMiIgdHJhbnNmb3JtPSJtYXRyaXgoMjUwLjg4NjU2IC0uODAyMjUgLjA1OTUxIDE4LjYxMDc2IDEzMC45IDEyLjUpIi8+PHBhdGggZmlsbD0iIzgyNWU1ZSIgZD0iTTc4IDgxaDEzMHY0SDc4eiIvPjxlbGxpcHNlIGN4PSIxMzAiIGN5PSI0NiIgZmlsbD0iIzc2M2Q1YSIgcng9Ijk5IiByeT0iMyIvPjxwYXRoIGZpbGw9IiM0ODZmOGEiIGQ9Ik00NSA1N2gxMzV2NEg0NXoiLz48cGF0aCBmaWxsPSIjMDQxNTJjIiBkPSJNMTE2IDYyaDE0MHYxOUgxMTZ6Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=166 300w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=278 500w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=389 700w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=500 900w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=612 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=166 300w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=278 500w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=389 700w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=500 900w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=612 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="556" srcset="https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=166 300w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=278 500w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=389 700w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=500 900w, https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=612 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/79n5IaIp7SeGoieSsZ4D17/2918d5b8664374ac0b0a581990f7a014/code-shot-anchored-container.png" alt="    @container anchored(fallback: bottom) {       padding: 24px 16px 16px 16px;       margin-top: 8px;       border-shape: var(--upwards-caret);     }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>And if that's not enough anchoring, <a href="https://www.joshwcomeau.com/css/anchor-positioning/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fwww.joshwcomeau.com%2Fcss%2Fanchor-positioning%2F')">Josh explains how to get started with anchor positioning</a>. But don't be fooled, the post gets into advanced topics like the <code>anchored</code> container query and <code>position-try-fallbacks</code> quickly.</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="inaccessible-grid-lanes" href="https://www.stefanjudis.com/blog/web-weekly-196/#inaccessible-grid-lanes">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="inaccessible-grid-lanes">Inaccessible grid lanes</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(100,215,231); --color2: rgb(175,86,92); --color3: rgb(252,116,116); --color4: rgb(172,180,196); --color5: rgb(48,67,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTUwIj48cGF0aCBmaWxsPSIjMzA0MzVjIiBkPSJNMCAwaDMwMHYxNTBIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgcj0iMSIgZmlsbD0iIzAwMDAwMyIgdHJhbnNmb3JtPSJyb3RhdGUoODYuNyA1Ny41IDEyMykgc2NhbGUoMTcxLjk0NjEyIDIwNi4xMDEzMykiLz48ZWxsaXBzZSBjeD0iOTIiIGN5PSI2MiIgZmlsbD0iIzNlMzQ1OSIgcng9IjUxIiByeT0iMTYiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiMwODE0MmUiIHRyYW5zZm9ybT0ibWF0cml4KDcxLjU5MDM4IDQwLjc3NTI3IC04NS44MTU4MyAxNTAuNjY5NDcgMjIyLjIgNjYuOSkiLz48ZWxsaXBzZSBjeD0iMTQyIiBjeT0iNjMiIGZpbGw9IiM1ZTQ2NmEiIHJ4PSI5MCIgcnk9IjMiLz48ZWxsaXBzZSBjeD0iMjMiIGN5PSI2OCIgZmlsbD0iIzA2MTEyOSIgcng9IjMwIiByeT0iMTI3Ii8+PHBhdGggZmlsbD0iIzdlNDM1NSIgZD0iTTQ2IDQwaDQ0djVINDZ6Ii8+PGNpcmNsZSByPSIxIiBmaWxsPSIjMDgxMjI1IiB0cmFuc2Zvcm09Im1hdHJpeCgtLjIwMzQzIDE2LjY1MDAzIC0xNTIuOTIzMTMgLTEuODY4NCAxMDYuNyAxMC44KSIvPjxlbGxpcHNlIGN4PSIxMDciIGN5PSI1MyIgZmlsbD0iIzVkNmM3YSIgcng9IjMzIiByeT0iMyIvPjwvZz48L3N2Zz4=');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="500" srcset="https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/66VR1uNRNBGXUob5zfcRtH/50839e2a0a395d38320e59ffae7bd33a/code-shot-grid-lanes.png" alt=".grid-lanes {   display: grid-lanes;   grid-template-columns: repeat(3, 1fr);   flow-tolerance: 0; }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>Safari shipped <code>display: grid-lanes</code>. Chromium's working on it. But what does it mean for accessibility when grid items are free-flowing and reordering themselves? Can an adjusted <code>flow-tolerance</code> help? Manuel asks the same questions and cautiously proposes a very interesting solution.</p>
<p><a class="btn btn__small" href="https://matuzo.at/blog/2026/grid-lanes-accessibility">Be aware</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="the-%60color-scheme%60-and-%60prefers-color-scheme%60-disconnect" href="https://www.stefanjudis.com/blog/web-weekly-196/#the-%60color-scheme%60-and-%60prefers-color-scheme%60-disconnect">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="the-%60color-scheme%60-and-%60prefers-color-scheme%60-disconnect">The <code>color-scheme</code> and <code>prefers-color-scheme</code> disconnect</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(98,229,245); --color2: rgb(168,95,119); --color3: rgb(250,186,116); --color4: rgb(172,180,192); --color5: rgb(49,76,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTM1Ij48cGF0aCBmaWxsPSIjMzE0YzVjIiBkPSJNMCAwaDMwMHYxMzVIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjE1MyIgY3k9IjEwMyIgcj0iMTg4IiBmaWxsPSIjMDAwMDAzIi8+PGNpcmNsZSBjeD0iMTQ0IiBjeT0iNTgiIHI9IjE1OCIgZmlsbD0iIzBhMTIzMCIvPjxjaXJjbGUgcj0iMSIgZmlsbD0iIzI5MzA0OCIgdHJhbnNmb3JtPSJtYXRyaXgoOTcuNDAxNyA0LjU5MzM1IC0uNzE2MTcgMTUuMTg2MjggMTIwLjYgNDkpIi8+PHBhdGggZmlsbD0iIzUxNWU3ZCIgZD0iTTUyIDU2aDE1MXY0SDUyeiIvPjxlbGxpcHNlIGN4PSIxMDQiIGN5PSIzNyIgZmlsbD0iIzYxM2Q0OSIgcng9IjY5IiByeT0iMyIvPjxlbGxpcHNlIGN4PSIxODQiIGN5PSIyNiIgZmlsbD0iIzBiMTgyYyIgcng9IjYzIiByeT0iMzEiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiMwYjE2MjgiIHRyYW5zZm9ybT0icm90YXRlKC04MSAyNy42IC0zMi4zKSBzY2FsZSgzMy40NzQxMSAxMDUuMjI1NTcpIi8+PGVsbGlwc2UgY3g9IjIwMCIgY3k9IjEwNSIgZmlsbD0iIzBhMTQyNCIgcng9IjI1NSIgcnk9IjE1Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=135 300w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=226 500w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=317 700w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=407 900w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=498 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=135 300w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=226 500w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=317 700w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=407 900w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=498 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="453" srcset="https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=135 300w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=226 500w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=317 700w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=407 900w, https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=498 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/2tZ0cB01dQEKToBrFojTl8/3fc0d1a24b21842bc2c39b989ccfcd73/code-shot-container-style.png" alt="@container style(--dark: false) { .card {     box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.2); } }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>Ollie described a fact that tripped me up big time:</p>
<blockquote>
<p>There’s an unfortunate disconnect between color-scheme and the prefers-color-scheme media query. prefers-color-scheme reflects the OS settings — regardless of the color-scheme value. If you’re providing an in-page toggle that implements dark mode, you can’t adopt the prefers-color-scheme media query.</p>
</blockquote>
<p>So what do you do about it? The post has some web platform answers.</p>
<p><a class="btn btn__small" href="https://olliewilliams.xyz/blog/dark-mode/">Use the new and fancy</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="linked-css-params-%F0%9F%98%B1" href="https://www.stefanjudis.com/blog/web-weekly-196/#linked-css-params-%F0%9F%98%B1">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="linked-css-params-%F0%9F%98%B1">Linked CSS params 😱</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(56,208,156); --color2: rgb(178,116,94); --color3: rgb(251,187,116); --color4: rgb(180,188,196); --color5: rgb(42,52,76);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTM3Ij48cGF0aCBmaWxsPSIjMmEzNDRjIiBkPSJNMCAwaDMwMHYxMzdIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjEzOCIgY3k9IjgwIiByPSIxNjYiIGZpbGw9IiMwMDA0MTQiLz48cGF0aCBmaWxsPSIjNmY2NDc5IiBkPSJNMzcgNzRoMTkxdjNIMzd6Ii8+PHBhdGggZmlsbD0iIzc1NTg3NCIgZD0iTTQyIDQwaDEyNHY0SDQyeiIvPjxlbGxpcHNlIGN4PSIxMjkiIGN5PSIxMTYiIGZpbGw9IiMwNjEyMjIiIHJ4PSIyNTUiIHJ5PSIyMiIvPjxlbGxpcHNlIGN4PSIxMjgiIGZpbGw9IiMwNjEzMjMiIHJ4PSIyNTUiIHJ5PSIyMiIvPjxwYXRoIGZpbGw9IiMwNjQ2NDYiIGQ9Ik0xMDIgNzJoNDh2NWgtNDh6Ii8+PGNpcmNsZSByPSIxIiBmaWxsPSIjMDkxNjI4IiB0cmFuc2Zvcm09Im1hdHJpeCg3LjczNTA4IC00MS43MTc3MyAyMi40MTY1OCA0LjE1NjM2IDI0Ny41IDYxLjMpIi8+PGVsbGlwc2UgY3g9IjciIGN5PSI1OSIgZmlsbD0iIzA5MTQyNSIgcng9IjE0IiByeT0iMTA2Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="460" srcset="https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=138 300w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=230 500w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=322 700w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=414 900w, https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=506 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/2XkW0d0twSv32IS8KEiUM0/01a688b1c927f27e27f004be4da3c401/code-shot-link-parameters.png" alt="img {   link-parameters: param(--color, green); }  .foo {   background-image: url(&quot;image.svg&quot;, param(--color, green)); }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>So! If you're dealing with SVG icons you probably know the pain of overwriting colors. <a href="https://patrickbrosset.com/articles/2026-06-22-whats-missing-from-svg/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fpatrickbrosset.com%2Farticles%2F2026-06-22-whats-missing-from-svg%2F')">Patrick listed missing SVG features</a> and as it turns out there's a new spec in the making that allows controlling CSS variables from "the outside". And I didn't test it, but he says that it's shipping in Firefox already!</p>
<p><a class="btn btn__small" href="https://drafts.csswg.org/css-link-params-1/">Control your SVGs</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="the-wonderful-weird-web-%E2%80%93-roland-50" href="https://www.stefanjudis.com/blog/web-weekly-196/#the-wonderful-weird-web-%E2%80%93-roland-50">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="the-wonderful-weird-web-%E2%80%93-roland-50">The wonderful weird web – Roland 50</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(238,217,16); --color2: rgb(132,148,140); --color3: rgb(247,238,137); --color4: rgb(196,196,188); --color5: rgb(45,60,72);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTczIj48cGF0aCBmaWxsPSIjMmQzYzQ4IiBkPSJNMCAwaDMwMHYxNzNIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxlbGxpcHNlIGN4PSIxODQiIGN5PSI3NSIgZmlsbD0iI2ZmZiIgcng9IjU4IiByeT0iMTQ2Ii8+PGVsbGlwc2UgY3g9IjE4MyIgY3k9IjU1IiBmaWxsPSIjZmZmIiByeD0iNTciIHJ5PSI0NSIvPjxlbGxpcHNlIGN4PSIxODUiIGN5PSIxNDQiIHJ4PSIyNTUiIHJ5PSI4Ii8+PGVsbGlwc2UgY3g9IjEwMCIgcng9IjI1MCIgcnk9IjEyIi8+PGNpcmNsZSBjeD0iNDQiIGN5PSI4NiIgcj0iODQiIGZpbGw9IiMyMjE0MDciLz48cGF0aCBmaWxsPSIjZmZmZmZjIiBkPSJNMTI4IDExMGgxMTF2MjdIMTI4eiIvPjxwYXRoIGZpbGw9IiNlYmY1YjIiIGQ9Im00Mi4yIDEwMC4yLS40IDI4LTIwLS40LjQtMjh6Ii8+PHBhdGggZD0iTTEzOCAxMDFoMjJ2MTVoLTIyeiIvPjwvZz48L3N2Zz4=');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=174 300w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=290 500w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=406 700w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=522 900w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=638 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=174 300w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=290 500w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=406 700w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=522 900w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=638 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="580" srcset="https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=174 300w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=290 500w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=406 700w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=522 900w, https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=638 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/4C6XJ4ofBIZNyp8WVtzIKO/37b05f26ade9165d37a94ddf2b7cca8e/image-shot-tr808-tb303.png" alt="Two synthesizer interfaces." loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>The music nerds will love this one!</p>
<p><a class="btn btn__small" href="https://roland50.studio/">Produce</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="deno-and-vercel-invest-in-desktop-apps%3F" href="https://www.stefanjudis.com/blog/web-weekly-196/#deno-and-vercel-invest-in-desktop-apps%3F">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="deno-and-vercel-invest-in-desktop-apps%3F">Deno and Vercel invest in desktop apps?</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(54.642857142857146,91.07142857142854,200.35714285714286); --color2: rgb(124,124,124); --color3: rgb(150.81428571428572,169.75714285714287,226.5857142857143); --color4: rgb(188,188,188); --color5: rgb(35,51,67);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTUwIj48cGF0aCBmaWxsPSIjMjMzMzQzIiBkPSJNMCAwaDMwMHYxNTBIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9Ijg1IiBjeT0iMzQiIHI9IjIwOCIgZmlsbD0iIzExMDkwNyIvPjxwYXRoIGZpbGw9IiM5YTk2OGYiIGQ9Ik00NiA0NGgxNzh2MzRINDZ6Ii8+PHBhdGggZD0iTTIxIDU1aDIyNHYxMEgyMXoiLz48Y2lyY2xlIHI9IjEiIHRyYW5zZm9ybT0ibWF0cml4KC0xNjguMTUxNzMgMS4xMTM4NCAtLjA3NjYgLTExLjU2NDgyIDEyOCAyOSkiLz48cGF0aCBmaWxsPSIjYjRiM2IzIiBkPSJNMzUgNjZoMTM2djExSDM1eiIvPjxjaXJjbGUgcj0iMSIgZmlsbD0iIzAwMDAwNCIgdHJhbnNmb3JtPSJtYXRyaXgoLTEuMzY0NTcgLTIxLjc1MzAzIDE1MC4zOTE2OCAtOS40MzQwNiA1OS4yIDEwMi44KSIvPjxwYXRoIGZpbGw9IiNiNGI0YjQiIGQ9Ik0xNDEgNDRoNzR2MTJoLTc0eiIvPjxwYXRoIGQ9Im0yMzUuOSAxMDEuNS0zMi44LTc3LjMgMjMtOS43IDMyLjggNzcuM3oiLz48L2c+PC9zdmc+');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="500" srcset="https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/4Ga2nU9ZPtkdAcIWiAmq89/af7a14ec058d4859e1d8a21672e9de74/image-shot-native-desktop-toolkit.png" alt="Toolkit for building native desktop apps" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>Honestly, I'm up for opinions here. <a href="https://deno.com/blog/v2.9#deno-desktop" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fdeno.com%2Fblog%2Fv2.9%23deno-desktop')">The new Deno release now supports building desktop apps</a>. When I saw this I was confused. Didn't Deno lay off people and isn't the money in providing hosting and infrastructure? Odd.</p>
<p>But then I also saw Vercel pushing for building Zig and web-based desktop apps with <a href="https://native-sdk.dev/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fnative-sdk.dev%2F')">Native SDK</a>?</p>
<p>Honestly, what is going on? Is this about market share? I don't get it.</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="wiggly-range-sliders" href="https://www.stefanjudis.com/blog/web-weekly-196/#wiggly-range-sliders">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="wiggly-range-sliders">Wiggly range sliders</h2></div><p><img src="https://images.ctfassets.net/f20lfrunubsq/41c5vROCWFczhakavpdUZg/5cfece1a0d711b6389fa88df7f5a9453/video-shot-wiggle.gif" alt="Three wiggle range sliders" loading="lazy"></p>
<p>What you see above aren't canvas or SVG elements. These three range sliders are classical <code>range</code> inputs with some CSS magic. I've got no idea how Temani comes up with that stuff.</p>
<p><a class="btn btn__small" href="https://css-tip.com/wiggly-range-slider/">Wiggle</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="new-things-in-ecmascript" href="https://www.stefanjudis.com/blog/web-weekly-196/#new-things-in-ecmascript">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="new-things-in-ecmascript">New things in ECMAScript</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(54.64285714285703,54.64285714285703,200.35714285714297); --color2: rgb(132,132,140); --color3: rgb(244,244,252); --color4: rgb(187,195,211); --color5: rgb(52,44,60);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgOTkiPjxwYXRoIGZpbGw9IiMzNDJjM2MiIGQ9Ik0wIDBoMzAwdjk5SDB6Ii8+PGcgZmlsbC1vcGFjaXR5PSIuNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLjYgLjYpIHNjYWxlKDEuMTcxODgpIj48Y2lyY2xlIGN4PSIxOTgiIGN5PSIzNSIgcj0iMjA0IiBmaWxsPSIjZmZmIi8+PGNpcmNsZSBjeD0iMTQwIiBjeT0iNDgiIHI9IjE1NyIgZmlsbD0iI2ZmZiIvPjxjaXJjbGUgY3g9IjEzMSIgY3k9IjU2IiByPSIxNDUiIGZpbGw9IiNmZmYiLz48cGF0aCBmaWxsPSIjMzgzNzQwIiBkPSJNMzYgNDBoMTU1djZIMzZ6Ii8+PGVsbGlwc2UgY3g9IjE0NSIgY3k9IjE5IiBmaWxsPSIjZmZmIiByeD0iMjU1IiByeT0iMTkiLz48ZWxsaXBzZSBjeD0iMTMzIiBjeT0iNjYiIGZpbGw9IiNmZmYiIHJ4PSIyNTUiIHJ5PSIyMCIvPjxwYXRoIGZpbGw9IiM4MDdmODUiIGQ9Ik05OSAzOGgxMDd2OEg5OXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJtMjAyLjYgMjAuNi01OSA0OS4yIDM4LjYtMjcuM0w2OC4xIDMxLjF6Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=99 300w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=166 500w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=233 700w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=299 900w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=366 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=99 300w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=166 500w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=233 700w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=299 900w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=366 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="333" srcset="https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=99 300w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=166 500w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=233 700w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=299 900w, https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=366 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/41fRHQvDQV4zd6ySRMCJ2C/ebf271df9e903b5157d5684b506f3b19/image-shot-ecmascript-2026.png" alt="What's new in ECMAScript 2026" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>It's already a tradition and I'm happy to see that Paweł is still summarizing the new JavaScript features. Check out what's coming to JavaScript.</p>
<p><a class="btn btn__small" href="https://pawelgrzybek.com/whats-new-in-ecmascript-2026/">Use the language</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="riding-the-cutting-edge-of-css" href="https://www.stefanjudis.com/blog/web-weekly-196/#riding-the-cutting-edge-of-css">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="riding-the-cutting-edge-of-css">Riding the cutting edge of CSS</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(97,226,243); --color2: rgb(172,84,92); --color3: rgb(252,116,116); --color4: rgb(172,180,196); --color5: rgb(53,67,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgOTEiPjxwYXRoIGZpbGw9IiMzNTQzNWMiIGQ9Ik0wIDBoMzAwdjkxSDB6Ii8+PGcgZmlsbC1vcGFjaXR5PSIuNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLjYgLjYpIHNjYWxlKDEuMTcxODgpIj48Y2lyY2xlIGN4PSIxNjMiIGN5PSI0MyIgcj0iMTgxIiBmaWxsPSIjMDAwMDAyIi8+PGNpcmNsZSByPSIxIiBmaWxsPSIjMDkxNjJmIiB0cmFuc2Zvcm09Im1hdHJpeCgxNTIuMjAwOTYgLTEzLjU0NzQ0IDguODc0ODggOTkuNzA2NCAxMjkgNDApIi8+PHBhdGggZmlsbD0iIzY2NWI3YSIgZD0iTTM2IDM3aDE4N3Y0SDM2eiIvPjxlbGxpcHNlIGN4PSI4NSIgY3k9IjMwIiBmaWxsPSIjODk0YTU3IiByeD0iNTIiIHJ5PSIyIi8+PHBhdGggZmlsbD0iIzBiMTcyOCIgZD0ibTM5LjMtMTggMy4zIDk0LTU0IDItMy4yLTk0eiIvPjxlbGxpcHNlIGN4PSIxNTMiIGN5PSI0IiBmaWxsPSIjMGExNDI0IiByeD0iMjU0IiByeT0iMTUiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiMwYTE0MjQiIHRyYW5zZm9ybT0ibWF0cml4KDEzMC42MjQyMiA4LjkzNzIgLTEuMTMyNDEgMTYuNTUxMSAxNjcuOCA3NykiLz48cGF0aCBmaWxsPSIjMTYyMjNiIiBkPSJNMjUgMTRoMTZ2NDVIMjV6Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=91 300w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=153 500w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=214 700w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=275 900w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=337 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=91 300w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=153 500w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=214 700w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=275 900w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=337 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="306" srcset="https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=91 300w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=153 500w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=214 700w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=275 900w, https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=337 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/54tlW2fv4NiVptsu5Fog7W/64ab01f497bf5e99eded836aec2c5104/code-shot-picker-calc-size.png" alt=".custom-select::picker(select) {   min-block-size: calc-size(fit-content, min(size, 12em)); }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>Jake went on a journey to "improve" the sizing behavior of customizable selects (Chromium-only so far) and oh boy... he goes into anchor positioning, <code>stretch</code> and <code>fit-content</code>, and <code>calc-size</code>. I'm sure you'll find more than one rabbit hole to dive into here.</p>
<p><a class="btn btn__small" href="https://jakearchibald.com/2026/goldilocks-select-height/">Select</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="rapid-fire" href="https://www.stefanjudis.com/blog/web-weekly-196/#rapid-fire">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="rapid-fire">Rapid fire</h2></div><ul>
<li><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fdevblogs.microsoft.com%2Ftypescript%2Fannouncing-typescript-7-0%2F')">TypeScript 7 is out</a>. And yes, that's the Golang rewrite.</li>
<li><a href="https://www.rfc-editor.org/info/rfc10008/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fwww.rfc-editor.org%2Finfo%2Frfc10008%2F')">The new <code>QUERY</code> HTTP method</a> moved from <code>draft</code> to <code>proposed standard</code>.</li>
<li><a href="https://developer.chrome.com/blog/usermedia-html-element" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fdeveloper.chrome.com%2Fblog%2Fusermedia-html-element')">Chrome ships a new <code>usermedia</code> element</a>.</li>
</ul>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="random-mdn-%E2%80%93-cross-document-view-transitions" href="https://www.stefanjudis.com/blog/web-weekly-196/#random-mdn-%E2%80%93-cross-document-view-transitions">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="random-mdn-%E2%80%93-cross-document-view-transitions">Random MDN – Cross-document view transitions</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(173,84,92); --color2: rgb(165,105,147); --color3: rgb(252,116,116); --color4: rgb(164,172,188); --color5: rgb(54,67,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTk5Ij48cGF0aCBmaWxsPSIjMzY0MzVjIiBkPSJNMCAwaDMwMHYxOTlIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjEwMSIgY3k9Ijc4IiByPSIyMDQiLz48cGF0aCBmaWxsPSIjODM1ZjhjIiBkPSJNODAgODJoMTA4djdIODB6Ii8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iODgiIHI9IjE4OCIgZmlsbD0iIzA3MTQyZSIvPjxwYXRoIGZpbGw9IiM5NDRlNWEiIGQ9Ik01NyA2M2gxMTZ2N0g1N3oiLz48cGF0aCBmaWxsPSIjNjE0ODY4IiBkPSJtNjQuNSA4Mi45IDU2LjctMi42IDEwMS4zIDMuMy0xMTQuOSA2LjZ6Ii8+PHBhdGggZmlsbD0iIzc1OTA5NiIgZD0iTTE2MSA4MWgzMXY3aC0zMXoiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjQgOTdoMnYxMWgtMnoiLz48cGF0aCBmaWxsPSIjYzJjZGQ4IiBkPSJNMTgyIDYyaDN2MTBoLTN6Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="666" srcset="https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=199 300w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=333 500w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=466 700w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=599 900w, https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=733 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/4IW8nV3WzxDBcaHkj6vdiE/7224709c799960b25cbf23e4106a28dd/code-shot-view-transition.png" alt="@view-transition {   navigation: auto; }" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>From the unlimited MDN knowledge archive...</p>
<p>Quick reminder, cross-document view transitions can be enabled with a few lines of CSS. Safari and Chromium ship them, so you can add some polish to your static sites. No harm done for Firefox users.</p>
<p><a class="btn btn__small" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@view-transition">Transition</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="til-recap-%E2%80%93-%60zoom%60" href="https://www.stefanjudis.com/blog/web-weekly-196/#til-recap-%E2%80%93-%60zoom%60">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="til-recap-%E2%80%93-%60zoom%60">TIL recap – <code>zoom</code></h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(68,127,188); --color2: rgb(124,132,132); --color3: rgb(172,204,228); --color4: rgb(180,188,188); --color5: rgb(60,76,92);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTc2Ij48cGF0aCBmaWxsPSIjM2M0YzVjIiBkPSJNMCAwaDMwMHYxNzZIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjEwMiIgY3k9IjYwIiByPSIxODYiIGZpbGw9IiNmZmYiLz48Y2lyY2xlIGN4PSIxMjMiIGN5PSI1NyIgcj0iMTc3IiBmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzAwMzZhNyIgZD0ibTE0MSA4Ni42LTEtMzIgMzUtMS4yIDEgMzJ6Ii8+PHBhdGggZmlsbD0iIzAwMmJhMiIgZD0iTTUyLjUgNTZ2MzJoLTMzVjU2eiIvPjxjaXJjbGUgY3g9IjE4NSIgY3k9Ijg4IiByPSIxMzMiIGZpbGw9IiNmZmYiLz48cGF0aCBmaWxsPSIjMDA0MTlkIiBkPSJNMTQwIDU0aDM1djM1aC0zNXoiLz48Y2lyY2xlIHI9IjEiIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0ibWF0cml4KC01LjUzODc1IC0zNy45NjY3MiAxMjUuMzY4NDMgLTE4LjI4OTI4IDQ1IDEyNi44KSIvPjxlbGxpcHNlIGN4PSIxMDkiIGN5PSIyNSIgZmlsbD0iI2ZmZiIgcng9IjI1NSIgcnk9IjMwIi8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=177 300w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=295 500w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=413 700w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=531 900w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=649 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=177 300w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=295 500w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=413 700w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=531 900w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=649 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="590" srcset="https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=177 300w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=295 500w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=413 700w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=531 900w, https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=649 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/2RxwW1ZK7GItpN0AaNN8el/455367c2ddc8138a5c69413fe43d295a/Screenshot_2026-07-13_at_10.14.17.png" alt="Two widgets showing the difference of `scale` and `zoom`." loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>A while ago I learned about valid use cases for the CSS <code>zoom</code> property. <code>zoom</code>? Yes, exactly. 😅</p>
<p><a class="btn btn__small" href="https://www.stefanjudis.com/today-i-learned/css-zoom-to-scale-elements/">Scale up</a></p>
<div class="highlightBox info margin-top-xl margin-bottom-xl">
      
          <div class="cornerBubble">
            <svg aria-hidden="true">
              <use xlink:href="/sprite.svg#icon-info"></use>
            </svg>
          </div>
        

      
      <p>Find more short web development learnings in <a href="https://www.stefanjudis.com/today-i-learned/">my "Today I learned" section</a>.</p>
</div><div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="automatic-baseline-updates" href="https://www.stefanjudis.com/blog/web-weekly-196/#automatic-baseline-updates">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="automatic-baseline-updates">Automatic baseline updates</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(4,132,196); --color2: rgb(124,124,132); --color3: rgb(148,198,225); --color4: rgb(191,199,212); --color5: rgb(60,60,68);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTgyIj48cGF0aCBmaWxsPSIjM2MzYzQ0IiBkPSJNMCAwaDMwMHYxODJIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9Ijk3IiBjeT0iMTM2IiByPSIyMTkiIGZpbGw9IiNmZmYiLz48Y2lyY2xlIGN4PSIxMzQiIGN5PSI3MSIgcj0iMTU5IiBmaWxsPSIjZmZmIi8+PGNpcmNsZSBjeD0iMTIzIiBjeT0iNjciIHI9IjE4MiIgZmlsbD0iI2ZmZiIvPjxjaXJjbGUgY3g9IjE2MSIgY3k9IjY0IiByPSIxOTciIGZpbGw9IiNmZWZmZmYiLz48cGF0aCBmaWxsPSIjMDAzY2FiIiBkPSJNMTk5IDEyOGgzN3YxMGgtMzd6Ii8+PHBhdGggZmlsbD0iI2I3YmVjMSIgZD0iTTExOCA1M2g1NXYyNWgtNTV6Ii8+PHBhdGggZmlsbD0iIzAwOTJjZCIgZD0iTTIwMiAxMjloMzZ2OWgtMzZ6Ii8+PHBhdGggZmlsbD0iIzBjNjg5ZCIgZD0iTTI0IDY2aDUxdjJIMjR6Ii8+PC9nPjwvc3ZnPg==');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=183 300w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=305 500w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=427 700w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=549 900w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=671 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=183 300w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=305 500w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=427 700w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=549 900w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=671 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="610" srcset="https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=183 300w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=305 500w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=427 700w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=549 900w, https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=671 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/YSRgRGM3tYwhRO2QssOZa/8c4e961927fe58252f52213ab5148006/image-shot-manage-notifications.png" alt="UI to configure baseline notifications." loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>I've got my own tooling for checking baseline updates, but if the Web Weekly updates aren't enough, <code>webstatus<wbr>.dev</code> now lets you set up baseline notifications.</p>
<p><a class="btn btn__small" href="https://webstatus.dev/">Stay up to date</a></p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="classifieds-%26-friends" href="https://www.stefanjudis.com/blog/web-weekly-196/#classifieds-%26-friends">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="classifieds-%26-friends">Classifieds &amp; friends</h2></div><p>My buddy Orçun is building <a href="https://useautoshelf.com/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fuseautoshelf.com%2F')">AutoShelf</a> and you should check it out if you want automated file organization.</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="three-valuable-projects-to-have-a-look-at" href="https://www.stefanjudis.com/blog/web-weekly-196/#three-valuable-projects-to-have-a-look-at">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="three-valuable-projects-to-have-a-look-at">Three valuable projects to have a look at</h2></div><ul>
<li><a href="https://github.com/ashleydavis/photosphere" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fgithub.com%2Fashleydavis%2Fphotosphere')">ashleydavis/photosphere</a> – A cross-platform application for managing your database of digital media files</li>
<li><a href="https://github.com/duongductrong/Snapzy" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fgithub.com%2Fduongductrong%2FSnapzy')">duongductrong/Snapzy</a> – An open-source native macOS screenshot and screen recording app.</li>
<li><a href="https://github.com/charmbracelet/glow" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fgithub.com%2Fcharmbracelet%2Fglow')">charmbracelet/glow</a> – Render markdown on the CLI.</li>
</ul>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="a-new-tiny-helper" href="https://www.stefanjudis.com/blog/web-weekly-196/#a-new-tiny-helper">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="a-new-tiny-helper">A new Tiny Helper</h2></div><p>
      </p><div class="sqip-container margin-top-l margin-bottom-l">
        <figure class="sqip-image" style="
          --color1: rgb(68.0000000000001,68.0000000000001,186.99999999999991); --color2: rgb(132,132,124); --color3: rgb(244,244,251); --color4: rgb(191,197,208); --color5: rgb(76,76,68);
          --sqip-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMDAgMTUwIj48cGF0aCBmaWxsPSIjNGM0YzQ0IiBkPSJNMCAwaDMwMHYxNTBIMHoiLz48ZyBmaWxsLW9wYWNpdHk9Ii41IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSguNiAuNikgc2NhbGUoMS4xNzE4OCkiPjxjaXJjbGUgY3g9IjE0OCIgY3k9IjczIiByPSIxNzAiIGZpbGw9IiNmZmYiLz48Y2lyY2xlIGN4PSI5OSIgY3k9IjMxIiByPSIxOTgiIGZpbGw9IiNmZmYiLz48Y2lyY2xlIGN4PSIxODciIGN5PSI0NCIgcj0iMjM0IiBmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzViNWE1NyIgZD0iTTgyIDUwaDg5djEwSDgyeiIvPjxjaXJjbGUgcj0iMSIgZmlsbD0iI2ZkZmZmZiIgdHJhbnNmb3JtPSJtYXRyaXgoLTM4LjI3MDQ5IC0xNy43ODEzNCA3Ny45MTEzMyAtMTY3LjY4NzI4IDIwMi43IDgzKSIvPjxjaXJjbGUgcj0iMSIgZmlsbD0iI2ZkZmZmZiIgdHJhbnNmb3JtPSJtYXRyaXgoNDguMjUxMiAtNS4zMjMwNiAxOS44NjUxIDE4MC4wNjg0OCAzNi43IDYyKSIvPjxlbGxpcHNlIGN4PSIxMjkiIGN5PSIyMyIgZmlsbD0iI2Y5ZmNmZiIgcng9IjExOCIgcnk9IjI2Ii8+PGVsbGlwc2UgY3g9IjEyMCIgY3k9Ijc0IiBmaWxsPSIjYTA5ZjliIiByeD0iMTAxIiByeT0iMyIvPjwvZz48L3N2Zz4=');
        ">
          <a href="https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png">
            <picture>
              <source type="image/avif" srcset="https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=avif&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <source type="image/webp" srcset="https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=webp&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px">
              <img width="1000" height="500" srcset="https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=300&amp;h=150 300w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=500&amp;h=250 500w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=700&amp;h=350 700w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=900&amp;h=450 900w, https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png?fm=jpg&amp;fit=scale&amp;q=75&amp;w=1100&amp;h=550 1100w" sizes="(max-width: 50em) 98vw, 700px" src="https://images.ctfassets.net/f20lfrunubsq/SDvwYsQuOR0Rgp6TKhKey/6e8961ff4c187cf707892da0f3602411/image-shot-ua-tracer.png" alt="ua-tracer — what does a user agent actually fetch, follow &amp; run?" loading="lazy" onload="this.classList.add('kf-fade-in')">
            </picture>
          </a>
        </figure>
      </div>
    <p></p>
<p>If you've ever wondered what specific analyzer tools, user agents, or browsers execute when they crawl sites, <code>ua-tracer</code> helps out!</p>
<p><a class="btn btn__small" href="https://uatracer.com/">Check what's happening</a></p>
<p>Side note: the last time I mentioned that I'm not really sure about Tiny Helper's future because frankly I'm drowning in vibe code submissions. Some of you replied telling me that you really like this section. So, what should I do with it?</p>
<p>For now, I closed contributions to the project and this section will become optional. I'm annoyed by this because I'm a big fan of consistency, but let's see how this plays out.</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="thought-of-the-week" href="https://www.stefanjudis.com/blog/web-weekly-196/#thought-of-the-week">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="thought-of-the-week">Thought of the week</h2></div><p>This is <a href="https://kittygiraudel.com/2026/04/29/you-cannot-spell-pain-without-ai/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fkittygiraudel.com%2F2026%2F04%2F29%2Fyou-cannot-spell-pain-without-ai%2F')">a take I can get very much behind</a>.</p>
<div class="highlightBox quote margin-top-xl margin-bottom-xl">
      
          <div class="cornerBubble">
            <svg aria-hidden="true">
              <use xlink:href="/sprite.svg#icon-quote"></use>
            </svg>
          </div>
        

      
      <blockquote>
<p>In an ideal world, the only AI-generated content I want to consume is the one I request myself.</p>
</blockquote>
</div><div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="did-you-learn-something-from-this-issue%3F" href="https://www.stefanjudis.com/blog/web-weekly-196/#did-you-learn-something-from-this-issue%3F">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="did-you-learn-something-from-this-issue%3F">Did you learn something from this issue?</h2></div><p>❤️ If so, <strong>join 53 other Web Weekly supporters</strong> and give back with a small monthly donation on <a href="https://www.patreon.com/stefanjudis" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fwww.patreon.com%2Fstefanjudis')">Patreon</a> or <a href="https://github.com/sponsors/stefanjudis/" style="--image-url: url('https://avatar.stefanjudis.com/https%3A%2F%2Fgithub.com%2Fsponsors%2Fstefanjudis%2F')">GitHub Sponsors</a>.</p>
<div class="heading-wrapper">
      <a class="margin-left-s text-no-underline" aria-label="Link to heading" aria-describedby="this-is-all%2C-friends!" href="https://www.stefanjudis.com/blog/web-weekly-196/#this-is-all%2C-friends!">
        <svg class="icon" aria-hidden="true">
          <use xlink:href="/sprite.svg#icon-link"></use>
        </svg>
      </a>
      <h2 id="this-is-all%2C-friends!">This is all, friends!</h2></div><p>Loved this email? Hated this email? I want to hear about it!</p>
<p>If you think something needs improvement or something sparked some joy, <a href="mailto:stefan@webweekly.email">reply to this email because I want to know more</a>!</p>
<hr aria-hidden="true"><p>And with that, take care of yourself - mentally, physically, and emotionally.</p>
<p>I'll see you next week! 👋</p>
</div>

          <hr>
          <a href="mailto:stefanjudis@gmail.com?subject=Re%3A%20Web Weekly #196">
            Reply to Stefan
          </a>
        

      ]]></description>
      <pubDate>Sun, 12 Jul 2026 22:00:00 +0000</pubDate>
      <link>https://www.stefanjudis.com/blog/web-weekly-196/</link>
      <dc:creator>Stefan Judis Web Development</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5304645888</guid>
    </item>
    <item>
      <title><![CDATA[MDN Traffic Browser]]></title>
      <description><![CDATA[<figure><a href="https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_.png"><img fetchpriority="high" decoding="async" src="https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_.png" alt="" width="560" height="294" class="alignnone size-medium wp-image-36427" srcset="https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_.png 2400w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-560x294.png 560w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-1120x588.png 1120w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-768x403.png 768w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-1536x806.png 1536w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-2048x1075.png 2048w, https://www.bram.us/wordpress/wp-content/uploads/2026/08/ready-unit-fowl.codepen.app_-1568x823.png 1568w" sizes="(max-width: 560px) 100vw, 560px"></a><figcaption>Screenshot of the visualizer, as <a href="https://ready-unit-fowl.codepen.app/">deployed onto CodePen</a></figcaption></figure>
<p>TIL: MDN exposes up-to-date page view data through <code>https://popularities.mdn.mozilla.net/current.csv</code>.</p>
<p>To visualize the data, I had <a href="https://aistudio.google.com/welcome">Google AI Studio</a> generate me a visualizer, which I deployed to CodePen</p>
<p class="text-center"><a href="https://ready-unit-fowl.codepen.app/" class="button button-big">Check out “MDN Traffic Browser” →</a></p>
]]></description>
      <pubDate>Mon, 03 Aug 2026 13:39:30 +0000</pubDate>
      <link>https://www.bram.us/2026/08/03/mdn-traffic-browser/</link>
      <dc:creator>Bram.us</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5332428150</guid>
    </item>
    <item>
      <title><![CDATA[In defense of polyfills]]></title>
      <description><![CDATA[<p>If you’re a web developer, you may find the title baffling.
<em>“Polyfills need defending? Who’s against them?!”</em> you might ask.</p>
<p>Two weeks ago, I’d be in the same boat.</p>
<p>Polyfills and I go way back.
I dug up a <a href="https://www.youtube.com/watch?v=V0wJ1BnfnR8">JSConf EU talk of mine from 2011</a> on exactly this topic.</p>
<figure>
<img src="https://lea.verou.me/blog/2026/polyfills/images/polyfilling-the-gaps.jpg" alt="YouTube still from that talk">
<figcaption>
Trying not to think about how young I look here 😅
</figcaption>
</figure>
<p>One of the few opinions I have held strongly over the years is that <strong><a href="https://en.wikipedia.org/wiki/Polyfill_(programming)">polyfills</a> are a net positive for the Web</strong>, and the good outweighs the rare failure cases where polyfills became too popular, too soon, and restricted the design space for the native API.</p>
<p>As with most things in life, it’s all about the cost-benefit.
<strong>We don’t stop flying planes because crashes happen.</strong>
We do a post-mortem and figure out how we can prevent the same accident from happening again.
And crucially, the point of a post-mortem is to find the root cause — not to ground the entire fleet.</p>
<p>Don’t get me wrong, concerns about polyfills are well-intentioned.
They come from implementors and standards folks who want to preserve the design flexibility to build the best API surface possible — a goal I share deeply.
Being both a spec editor and a library author, striking that balance is something I navigate all the time.</p>
<p>Still, I was under the impression that seeing polyfills as a net positive was the consensus view of the web standards community as a whole.
That while we may not have consensus on the specific tradeoffs or solutions, we see polyfilling as a good thing and we generally <em>do</em> want web platform features to be polyfillable.</p>
<p>So, you can imagine my surprise when in a <a href="https://docs.google.com/document/d/1D90QugzZZM6ga8k0ChMA7llmGFHcH9quDj4oCrgqtkE/edit?tab=t.0">recent WHATWG meeting</a> where I presented a proposal for <a href="https://github.com/whatwg/dom/issues/1287">extending mutation observers to observe shadow root attachment</a>, <strong><a href="https://annevankesteren.nl/">Anne van Kesteren</a> expressed the view that polyfilling is harmful</strong>.</p>
<!-- read more -->
<figure>
<img src="https://lea.verou.me/blog/2026/polyfills/images/say-what-now.gif" alt="Say what now?!">
</figure>
<p>It’s important to note that Anne is not some rando.
He is the main active editor of most WHATWG specs (HTML, DOM, Fetch, import maps, etc.), a WebKit engineer at Apple, and has <strong>tremendous overall influence</strong> on the direction of the Web platform.</p>
<p>I had to find out if Anne’s comment reflected broader committee consensus, so
I <a href="https://matrix.to/#/!AGetWbsMpFPdSgUrbs:matrix.org/$MQ9QZ1sfSozpC6GsNpzX-v9srz7QXqAI6z1cuBfKDiU?via=matrix.org&amp;via=mozilla.org&amp;via=igalia.com">brought the topic up in the WHATWG Matrix</a> and asked some folks privately.</p>
<p>Thankfully, it became clear that no, <strong>Anne’s comment did <em>not</em> reflect broader consensus</strong> (😮‍💨), but some of the views expressed about polyfills were more ambivalent than I’d have expected.</p>
<p>As I said, these concerns are well-intentioned — but I believe they are generally misplaced.
Most fail to consider the system holistically, and especially the human factors that drive developer behavior.</p>
<p>Let me explain.</p>
<h2 id="it%E2%80%99s-not-the-polyfills" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#it%E2%80%99s-not-the-polyfills">It’s not the polyfills</a></h2>
<p><strong>Authors want to use native behavior when it exists, without breakage when it doesn’t.</strong>
That’s the crux of it.
<em>That’s</em> what restricts design flexibility.</p>
<img src="https://lea.verou.me/blog/2026/polyfills/images/polyfills-pe.jpg" alt="Scooby do mask reveal meme with the first panel reading “Polyfills” and the second (revealed) reading “Progressive Enhancement”">
<p>Whenever this happens too early, by too many people, you can have a problem.
It doesn’t matter whether the code using it is a polyfill, a fallback, “progressive enhancement” or “graceful degradation”.</p>
<p>Fundamentally, <strong>every pattern that uses the native feature when it exists and a userland implementation (or even nothing) when it doesn’t</strong> risks breakage if the feature changes in a way that is incompatible with that usage.</p>
<p>This includes:</p>
<ul>
<li>Using CSS properties for progressive enhancement, accepting that they won’t work everywhere</li>
<li>CSS fallbacks via the cascade or <code>@supports</code></li>
<li>Conditional imports after feature detection</li>
<li>Using a modern HTML element, with a script that converts it to (or wraps it with) a web component if not supported</li>
<li>Build tools that include fallbacks alongside the native feature</li>
<li>JS <code>let foo = nativeThing?.() ?? fallbackThing()</code></li>
<li>etc etc</li>
</ul>
<p>In <em>all</em> of these cases, if the native API were to change in a way incompatible with the fallback usage, stuff could break.</p>
<blockquote class="pullquote">Eliminating polyfills does not eliminate the need for them.</blockquote>
<p>The benefit of polyfills over the techniques above is author-facing: polyfills are portable, maintainable, and typically more correct than ad-hoc fallbacks.</p>
<p>When a bug is identified in a polyfill, it can be fixed centrally.
When a bug is identified in an ad-hoc fallback strategy, good luck finding all the callsites and fixing them.</p>
<p>Bottom line: <strong>eliminating polyfills does not eliminate the need for them</strong> and all other avenues of satisfying that need are measurably <em>worse</em>.</p>
<h2 id="polyfills-decouple-api-design-from-implementation" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#polyfills-decouple-api-design-from-implementation">Polyfills decouple API design from implementation</a></h2>
<p><strong>Standardized APIs have intrinsic value, independently of browser implementations.</strong></p>
<p>We think of polyfills as a toggle: use a polyfill, leave it in there, and later when all browsers implement the feature natively, it gets removed.</p>
<p>This misses out on one of the biggest benefits of polyfills: <strong>decoupling API design from implementation</strong>.</p>
<p>If a userland library isn’t working well, you must weigh the tradeoffs of refactoring your codebase to use a different library, and educating your team about the new library.</p>
<p>With a polyfill, <strong>the API itself is not within the purview of the polyfill</strong>.
The polyfill supplies only implementation, while the API is externally decided by the standards.
Thus, swapping a misbehaving polyfill for another has near-zero cost.</p>
<p>The benefits of API standardization are far greater when we consider the broader ecosystem.
A dependency isn’t aware what your userland implementation may be for certain functionality.
Either it needs to provide a way to pass it via configuration (expanding its API surface), or it needs to implement its own version of the functionality, which may be incompatible with that of the host.</p>
<p><strong>Standardized APIs are the town square of the Web</strong>, uniting all consumers (developers, agents, tooling, etc) around a shared vocabulary.</p>
<p>For example, suppose we’re using:</p>
<ul>
<li>a data binding framework that synchronizes JS data with form controls</li>
<li>a library that reads form control values and stores them in <code>localStorage</code> until the form is submitted</li>
<li>a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Invoker_Commands_API">custom command invoker</a> for copying the value of an arbitrary form control to the clipboard</li>
</ul>
<p>As long as these are implemented in an element-agnostic way, reading <code>element.value</code> <sup class="footnote-ref"><a href="https://lea.verou.me/blog/2026/polyfills/#fn1" id="fnref1">[1]</a></sup> and listening to <code>input</code> and <code>change</code> events, they will work with any form control that implements the same API, whether it’s a native HTML element or a custom element, allowing for <a href="https://en.wikipedia.org/wiki/Loose_coupling">loose coupling</a>.</p>
<p>But without this standardized API, we need to specify how every custom element expresses its data and what event(s) it uses to notify for changes to each and every one of these libraries.
This is user effort on the magnitude of <strong>O(M × N) on the number of consumers and producers</strong> that must know about each other.
<strong>Standardized APIs collapse it to O(1).</strong></p>
<h2 id="are-separate-namespaces-the-solution%3F" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#are-separate-namespaces-the-solution%3F">Are separate namespaces the solution?</a></h2>
<p>From the same <a href="https://docs.google.com/document/d/1D90QugzZZM6ga8k0ChMA7llmGFHcH9quDj4oCrgqtkE/edit?tab=t.0">WHATWG discussion</a>:</p>
<blockquote>
<p><strong>Anne:</strong> I would much rather people write a library that demonstrates the need for something as opposed to something that attempts to mimic the exact API shape of a proposal while simultaneously trampling all over our design flexibility.</p>
</blockquote>
<blockquote>
<p><strong>Anne:</strong> You want to enable people using the amount element? Fine, but call it [something]-amount.</p>
</blockquote>
<blockquote>
<p><strong>Stephen:</strong> I see “library that implements some feature” and “polyfill” as synonymous, but I can also see that fails to capture everything.</p>
</blockquote>
<blockquote>
<p><strong>Anne:</strong> When I hear polyfill I think specifically of things that attempt to occupy the exact same API space and tend to cause issues, such as we had recently with Scoped Custom Element Registries. We ended up with a much worse standardized API because of that.</p>
</blockquote>
<p>The topic of polyfilling often comes up in discussions about CSS and HTML polyfilling, which are currently much harder to polyfill than JS features.</p>
<p>Many people are of the opinion that the solution is to have a separate namespace for polyfills, so they cannot conflict with native implementations.
For example, instead of polyfilling <code>&lt;amount&gt;</code>, you’d create a web component that polyfills <code>&lt;polyfill-amount&gt;</code>.
Instead of polyfilling the CSS property <code>border-shape</code>, you’d write a polyfill that makes <code>--border-shape</code> work the same way.</p>
<p>At first glance, this seems ideal: the benefits of polyfilling without the risk of syntax lock-in!
But we’ve tried it before.</p>
<p><strong>Who remembers vendor prefixes?</strong></p>
<p>Their core premise was exactly the same: if we experiment in a separate namespace, the native implementation can change without breaking any existing deployed experiments!</p>
<p>But remember, <strong>humans want to use the native feature when it’s there</strong>.
And they don’t want to go back and edit their code when that happens.
So what do they do?
They use both!
Even if the native feature has no implementations yet, pre-emptively.</p>
<p>We know exactly how this plays out, because it already has: stylesheets everywhere declared <code>-webkit-border-radius</code> with an unprefixed <code>border-radius</code> right below it — before any browser shipped the unprefixed property, and before its syntax was final.</p>
<p>So authors get worse DX, and web standards get no more design flexibility.
Everybody loses.</p>
<h2 id="is-dropping-feature-detection-the-solution%3F" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#is-dropping-feature-detection-the-solution%3F">Is dropping feature detection the solution?</a></h2>
<p>As another attempt to mitigate the issues, some have started recommending polyfills that skip feature detection entirely and <em>always</em> use the fallback implementation, even when the native feature exists.</p>
<p>This is extremely wasteful.
Polyfills add weight, are typically slower than native implementations, less accessible, less i18nclusive, and rarely handle edge cases.
As their name implies, they are meant to <em>temporarily</em> cover a gap.
They were never meant to be a long-term solution.
The whole point is planned obsolescence: they eventually become dormant, even in a codebase that is no longer actively maintained.
Feature detection and conditional loading are <em>essential</em> for that to work.</p>
<p>Authors accept these costs <em>because</em> they are temporary: every browser update quietly moves more of their users onto the native implementation, until the polyfill fades into a no-op.
Remove feature detection, and the costs become permanent.
Every user pays the download forever, and no user ever gets the native implementation’s performance, accessibility, or i18n — even when it’s sitting right there in their browser.</p>
<p>This approach does preserve the advantages of a standardized API, but at what cost?
It trades away real, tangible benefits, felt by every user of the Web, to prevent a rare, largely theoretical risk.
<strong>Every economist would be pulling their hair out at this cost-benefit!</strong></p>
<h2 id="are-ponyfills-the-solution%3F" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#are-ponyfills-the-solution%3F">Are ponyfills the solution?</a></h2>
<p><a href="https://ponyfill.com/">Ponyfills</a>, as originally envisioned by <a href="https://sindresorhus.com/">Sindre Sorhus</a>, are basically userland libraries that closely track the native API, but do not modify the global environment and may intentionally diverge from the spec.</p>
<p>They are often suggested as a better alternative to polyfills.</p>
<p>For example, a polyfill for <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape"><code>RegExp.escape()</code></a> might look like this:</p>
<pre><code class="language-js">if (!RegExp.escape) {
	RegExp.escape = function (s) {
		return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&amp;');
	};
}
</code>
</pre>
<p>Whereas a ponyfill might be:</p>
<pre><code class="language-js">export function regexpEscape(value) {
	return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&amp;');
}
</code>
</pre>
<p>Note that <strong>this does not use the native implementation at all</strong>.
It is not meant to be removed later.
Its link to the native API is purely psychological.</p>
<p>I can see ponyfills being helpful for iterating on and getting feedback on an API while the standard is still being developed, or even to motivate a new proposal.
But then again, so are userland libraries.</p>
<p>But for a mature feature, that has shipped in at least one browser, their appeal is much weaker.</p>
<p>They lack all strengths of polyfills:</p>
<ul>
<li>They don’t use the native implementation, even when it exists.</li>
<li>They cannot be removed without refactoring</li>
<li>Without a standardized API, a ponyfill cannot be swapped for another without further refactoring.</li>
<li>They introduce a new userland dependency that needs to be evaluated, learned, documented, and maintained, just the same as if no standard existed at all.</li>
</ul>
<p><strong>IMO ponyfills are just userland libraries with better marketing.</strong></p>
<p>Even their only advantage, not modifying the global environment and thus not trampling on the spec, is typically negated by actual usage: as a sort of …deconstructed polyfill.</p>
<p>See <a href="https://kikobeats.com/polyfill-ponyfill-and-prollyfill/">this post</a> where the author is using a ponyfill for <code>Number.MAX_SAFE_INTEGER</code> as part of a makeshift polyfill:</p>
<pre><code class="language-js">const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || require('number-max-safe-integer');
</code>
</pre>
<p><strong>This is the norm</strong>, not some lone author failing to grok the concept of ponyfills.
In fact, many people would call something like this a ponyfill as well:</p>
<pre><code class="language-js">export function regexpEscape(value) {
	if (RegExp.escape) {
		return RegExp.escape(value);
	}

	return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&amp;');
}
</code>
</pre>
<p>This combines the disadvantages of both approaches, while getting you the benefits of neither!</p>
<p><strong>In the end, the usage hasn’t changed, because the human need hasn’t changed.</strong></p>
<h2 id="do-polyfills-discourage-native-implementations%3F" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#do-polyfills-discourage-native-implementations%3F">Do polyfills discourage native implementations?</a></h2>
<blockquote>
<p><strong>Stephen:</strong> […] polyfills can reduce the need for all browsers to implement something in a timely fashion.</p>
</blockquote>
<p><em>Do they?</em></p>
<p>Yes, the existence of high-quality, lightweight polyfills <em>can</em> reduce the urgency for browsers to be the <a href="https://webstatus.dev/stats#missing-one-implementation">last to implement</a>.
But even then, the pressure never drops to zero: polyfills are slower and heavier than native implementations, and it’s the lagging browser’s own users who feel the difference.
Without polyfills, authors wouldn’t use the feature at all — every browser would be equally fast at not supporting it, and the laggard could drag its feet indefinitely, consequence-free.</p>
<p>More importantly though, being last is not what’s critical for the Web’s evolution.</p>
<p>Check out <a href="https://lea.verou.me/specs">my specs page</a>.
What is the average time from spec to first implementation, from that to the next, and from that to the last?</p>
<table>
<thead>
<tr>
<th>Milestone</th>
<th>Proposals</th>
<th>Mean time</th>
<th>Median time</th>
</tr>
</thead>
<tbody>
<tr>
<td>Spec → first implementation</td>
<td>9</td>
<td>1y 9m</td>
<td>7m 1d</td>
</tr>
<tr>
<td>First → second implementation</td>
<td>10</td>
<td>5m 10d</td>
<td>3m 5d</td>
</tr>
<tr>
<td>Second → last implementation</td>
<td>8</td>
<td>6m 14d</td>
<td>4m</td>
</tr>
</tbody>
</table>
<p><strong>The first implementation is the hardest.</strong></p>
<blockquote class="pullquote">A Web platform without polyfills would be a stagnant platform.</blockquote>
<p>What is the motivation for a browser to be the first to implement if authors cannot use the feature until all others implement it as well?
Would <em>you</em> sink millions of dollars of engineering time into something your competitors can render moot, by means of simply …doing nothing?</p>
<p>We cannot A/B test reality, so this is purely a thought experiment, but I think if polyfills, fallbacks, and progressive enhancement suddenly disappeared, it would <em>slightly</em> increase motivation for those shipping last, but would <em>significantly</em> reduce motivation for shipping first.
And since nobody can be last without someone else being first, <strong>a Web platform without polyfills would be a stagnant platform.</strong></p>
<p>Additionally, often the biggest blocker in getting browsers to implement new features is <strong>demonstrating developer demand</strong>.
A popular polyfill does that beautifully.</p>
<p>In fact, there is already plenty of precedent that if a feature cannot be used conditionally, browsers are reluctant to implement it.
For example, decorators consistently top the list of <a href="https://2025.stateofjs.com/en-US/features/#js_new_features">missing JS features</a>, but because JS syntax cannot be reasonably polyfilled, they are nontrivial to implement, and they are so widely used via transpilers, no browser has shipped them.
“Let them use build tools” seems to be the general unspoken consensus.</p>
<h2 id="polyfills-only-get-in-the-way-when-the-standards-process-fails" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#polyfills-only-get-in-the-way-when-the-standards-process-fails">Polyfills only get in the way when the standards process fails</a></h2>
<p>I did some digging;
Anne’s view traces back to a <a href="https://github.com/whatwg/html/issues/11076#issuecomment-2687891140">2025 whatwg/html discussion</a> where an API around scoped custom element registries had to be renamed due to conflicts with two existing polyfills that implemented a different behavior (emphasis mine):</p>
<blockquote>
<p>We have prototyped a solution that seems to work and preserves the API pretty much as-is <strong>(still unfortunate though; please stop polyfilling and deploying polyfills)</strong></p>
</blockquote>
<p>To be fair, the underlying concern is real, so let me steelman it.
Once a library squatting a proposed API name becomes popular enough, its exact behavior — bugs and all — becomes a web compat constraint.
Standards groups then face a choice between adopting the library’s semantics or breaking deployed sites, and sometimes the only way out is renaming or redesigning the native feature, which is what happened here.
That is a real cost, borne by the very people designing the platform.</p>
<p>But let’s be clear: <strong>none of the libraries that caused this problem was actually a polyfill</strong>, since there was no standardized API <em>to</em> polyfill.</p>
<p>At worst, they were naughty userland libraries, much like MooTools’ <code>Array.prototype.flatten()</code> that caused the <a href="https://medium.com/@jacobdfriedmann/smooshgate-the-ongoing-struggle-between-progress-and-stability-in-javascript-2a971c1162dd">smooshgate</a> fiasco.
At best, they were speculative polyfills (aka <a href="https://www.w3.org/2001/tag/doc/polyfills/#nomenclature-what-is-a-polyfill-"><em>prollyfills</em></a>) — an imagined API occupying the same namespace as a future builtin so authors can experiment with an emerging standard.
Prollyfills are (rightly) more controversial, and not what this post is about.</p>
<blockquote class="pullquote">Assigning blame to polyfills is like blaming car accidents on the ambulances that show up on the scene.</blockquote>
<p>But when you examine the failure cases more closely, they all have something in common:
In every single one, <strong>standards groups and/or browsers failed to react to a strong user need in a timely fashion</strong>.</p>
<p>Assigning blame to polyfills is like blaming car accidents on the ambulances that show up on the scene.
<strong>A popular polyfill is a consequence.</strong>
Polyfills should never become too popular in the first place: the feature should be widely implemented by then!</p>
<p>If a polyfill (or prollyfill) becomes so popular that it can create a compat problem, <strong>that is a symptom that a pervasive user need went unmet for too long</strong>.
Whether it’s a polyfill or a prollyfill only changes which entity failed to react in time.</p>
<p>And even in the failure cases, consider the counterfactual.
When a prollyfill constrains the design space, the damage is a worse name or a compromised API shape — bounded, and known <em>before</em> shipping.
When an API ships without real-world validation, the damage is unbounded, and only discovered once the design is frozen into the platform by web compat.
Remember <a href="https://en.wikipedia.org/wiki/Cache_manifest_in_HTML5">AppCache</a>?
It shipped natively with no userland trial run, turned out to be <a href="https://alistapart.com/article/application-cache-is-a-douchebag/">fundamentally wrong</a>, and took a decade to deprecate and replace with Service Workers.
<strong>A prollyfill that surfaces design problems while the spec can still change is far cheaper than deploying the wrong API.</strong></p>
<p>Developers don’t use polyfills in vain.
Every dependency has a cost.
If we see a polyfill becoming wildly popular, <strong>that should be a signal for urgent prioritization</strong>, not a reason to complain that authors are naughty.</p>
<p>Instead of <strong>blaming the user</strong> (“<em>please stop polyfilling and deploying polyfills</em>”), we should be doing a post-mortem on how to avoid such process failures in the future.</p>
<blockquote>
<p>“If one user gets it wrong, it <em>might</em> be them.
If two users get it wrong, it’s <em>definitely</em> you”
— Ancient UX proverb<sup class="footnote-ref"><a href="https://lea.verou.me/blog/2026/polyfills/#fn2" id="fnref2">[2]</a></sup></p>
</blockquote>
<p>When web platform design follows the <a href="https://www.w3.org/TR/design-principles/#priority-of-constituencies">priority of constituencies</a>, and prioritizes end-user and author needs above implementors and standards authors, the failure cases are practically nonexistent, <strong><em>even</em> when polyfills jump the gun and ship too early</strong>.</p>
<h3 id="the-whatwg-process-invites-these-failures" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#the-whatwg-process-invites-these-failures">The WHATWG process invites these failures</a></h3>
<p><strong>Why <em>did</em> scoped registries take so long to ship?</strong>
The web components community had been asking for years!</p>
<p>Part of this is that the WHATWG process is designed to be <em>reactive</em> rather than <em>proactive</em>.
Even if there is a strong, demonstrated author need, WHATWG will refuse to flesh out a feature until <a href="https://whatwg.org/working-mode#additions">at least two implementors express interest</a> in implementing it.
<a href="https://github.com/whatwg/html/issues?q=sort%3Aupdated-desc%20is%3Aissue%20state%3Aopen%20label%3A%22needs%20implementer%20interest%22">“needs implementer interest”</a> is where so many good ideas go to die.</p>
<p>This made sense when WHATWG was founded as a reaction to the increasingly academic W3C HTML Working Group, which had been designing XHTML 2 in a vacuum, speccing features that no browser was willing to implement.</p>
<p>But having spent 15 years in the CSS WG and having proposed and/or helped drive <a href="https://lea.verou.me/specs">several</a> features to Baseline, I cannot <em>imagine</em> gating spec development on implementor interest.
That’s putting the cart before the horse!</p>
<p><strong>Demonstrating developer need is <em>exactly</em> what drives implementor interest in the first place!</strong>
And that’s much easier to do with a feature that is actively being worked on.</p>
<p>For one, researching use cases and prior art to flesh out the feature often demonstrates author need in itself.
But also, <strong>users have a lot of trouble expressing abstract pain points.</strong>
It is much easier to point to an existing feature and say <em>“I want browsers to support this”</em> than to express a need for which no feature exists.</p>
<p>In a way, polyfills are to the web platform what user testing is to product design — and the prototype part isn’t even an analogy: a polyfill literally <em>is</em> a prototype of the feature.
Real user feedback, <strong>without baking technical debt into the platform</strong>.
One could even argue that browsers should be funding — or outright developing — these polyfills, precisely so they can gather this feedback <em>before</em> an API is frozen into the platform by web compat.
Yes, there is some risk they may constrain the ergonomics of the eventual API — but if high-demand features actually ship at a reasonable pace, that risk shrinks dramatically.</p>
<h2 id="polyfills-restore-power-balance-in-the-ecosystem" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#polyfills-restore-power-balance-in-the-ecosystem">Polyfills restore power balance in the ecosystem</a></h2>
<p>Who remembers <a href="https://code.google.com/archive/p/ie7-js/">IE7.js</a>? Or <a href="https://github.com/aFarkas/html5shiv">html5shiv</a>?</p>
<p>For nearly a decade, IE was the boat anchor of the Web: dominant market share, no meaningful competition, and no incentive to implement anything new.
Without polyfills, the rational strategy for many authors was to target IE and call it a day — and the rational strategy for every other browser would have been to stop investing in features nobody could use.</p>
<p>Instead, polyfills allowed authors to adopt modern standards <em>years</em> before IE supported them.
That kept demand for standards alive, sustained pressure on IE, and gave end-users reasons to switch browsers.
<strong>The fact that we were finally able to move away from IE6 and IE7 is in large part thanks to polyfills.</strong></p>
<p>A browser lagging behind is not always a matter of will, either — <strong>release cadence matters</strong>.
IE, and EdgeHTML after it, was bound to the Windows release schedule, just as Safari is tied to the macOS and iOS release trains today — a constraint the engineers working on these engines have no say in.
A slow release train stretches exactly the gap that polyfills exist to cover: even once a feature ships, it reaches users in OS-update time, not browser-update time.
If every engine could ship at the cadence of Chromium and Firefox, the need for polyfills would shrink significantly.
Until then, polyfills are how authors deliver the benefits of native APIs — performance, accessibility, i18n — without being held hostage to the slowest release train.</p>
<blockquote class="pullquote">Without polyfills, a single browser has undue power to hold back the Web.</blockquote>
<p>Without polyfills, fallbacks, and progressive enhancement, <strong>a single browser has undue power to hold back the Web</strong> — whether by choice or by circumstance.
I’m <em>far</em> more concerned about that than about the occasional smooshgate.</p>
<h2 id="polyfills-make-the-web-faster%2C-inclusive%2C-and-more-robust" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#polyfills-make-the-web-faster%2C-inclusive%2C-and-more-robust">Polyfills make the Web faster, inclusive, and more robust</a></h2>
<p>Native features are typically more performant, more accessible, more i18nclusive, and handle more edge cases than <em>any</em> userland implementation.</p>
<p>A userland library can cut corners, exclude locales, declare that a11y is out of scope, and fail to consider edge cases.
A web standard cannot.</p>
<p>A polyfill inherits <em>some</em> of these limitations while it’s active, but unlike a userland library, it is graded against a spec: its target behavior has already been through accessibility and i18n review, so its gaps are well-defined, visible, and centrally fixable.
A userland library gets to define its own bar.</p>
<p><strong>Developers being able to use web features before wide availability is a win for the Web as a whole.</strong></p>
<h2 id="can-we-stop-trying-to-solve-the-wrong-problem%3F" tabindex="-1"><a class="header-anchor" href="https://lea.verou.me/blog/2026/polyfills/#can-we-stop-trying-to-solve-the-wrong-problem%3F">Can we stop trying to solve the wrong problem?</a></h2>
<p>Cost-benefit analysis is a fundamental part of human decision-making.<br>
We get in cars, even though there is a risk of accidents.<br>
We go outside, even though we may catch a cold — or worse.<br>
We swim, even though there is a small risk of drowning.<br></p>
<p>We weigh the extent of the risk, the probability of it happening, and the cost of mitigating it, against the extent and frequency of the benefits, and decide accordingly.
We do this all the time, whether consciously or not.</p>
<p>When it comes to the Web, the benefits of polyfills are tangible and felt by everyone who develops for it.
Historically, failure cases have been few, and generally have been mitigated well.
Even smooshgate resulted in <code>array.flat()</code>, which to me is not obviously worse than <code>array.flatten()</code>.</p>
<p>We are spending so much collective energy trying to mitigate a largely theoretical problem, that has only been a significant issue a handful of times in the 30+ years of the Web’s history.
And yet, many are willing to sacrifice significant, tangible, far-reaching benefits to do so.
This is an emotional reaction to a few high-profile incidents, not a rational cost-benefit tradeoff.</p>
<p>Instead of trying to eliminate polyfills, we should be more proactive in discovering and reacting to developer needs, so polyfills never become so popular that they threaten the design space of a native API.</p>
<p><strong>Starting from polyfilling <em>itself</em>.</strong> <sup class="footnote-ref"><a href="https://lea.verou.me/blog/2026/polyfills/#fn3" id="fnref3">[3]</a></sup></p>
<p><em>Huge thanks to <a href="https://www.linkedin.com/in/gregwhitworth/">Greg Whitworth</a> and <a href="https://allons-y.llc/">Cassondra Roberts</a> for reviewing an earlier draft of this post.</em></p>
<!--
## Gaps in polyfilling

By not working on primitives that make good polyfills possible we do not preserve our design flexibility, we just get a buggier Web…

**Polyfills will still be written, they will just work less well.**

This is not a granular concern.
I’m not suggesting we consider polyfillability for every single feature we design.
But at the web technology level, it should absolutely be a goal to make polyfilling viable.

As a bonus, many of the gaps that make polyfilling difficult are also gaps that make userland libraries and frameworks more difficult to implement correctly.

### Components of a polyfill

- Feature detection
- Detecting current callsites
- Monitoring future callsites

### JS polyfills

JS is the most polyfillable of the web platform languages.
Most runtime behaviors can be polyfilled.

Except syntax.
And since JS fails loudly, new syntax cannot just be used alongside old syntax like in CSS or HTML.

There are even polyfills that transpile syntax at runtime, such as [Guy Bedford](https://guybedford.com/)’s [es-module-shims](https://www.npmjs.com/package/es-module-shims)
which polyfilled import maps and other import-related features.

CSS polyfills currently require refetching and reparsing the entire stylesheet, making individual polyfills effectively unviable
HTML polyfills that work correctly across shadow DOM boundaries are practically impossible to implement performantly because of the lack of hooks to monitor (even open) shadow roots, monitor mutations across shadow roots, or fetch elements that match a certain pattern across shadow roots. -->
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>Special-casing known offenders like radios and checkboxes of course. <a href="https://lea.verou.me/blog/2026/polyfills/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p>No, not really, I made this up, but the takeaway is pretty established in HCI. <a href="https://lea.verou.me/blog/2026/polyfills/#fnref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn3" class="footnote-item"><p>Stay tuned: in Part 2 I’ll discuss what gaps in the Web platform make it hard to write good polyfills today, and how this varies across HTML, CSS, and JS. <a href="https://lea.verou.me/blog/2026/polyfills/#fnref3" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
]]></description>
      <pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://lea.verou.me/blog/2026/polyfills/</link>
      <dc:creator>Lea Verou’s blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5305221602</guid>
    </item>
    <item>
      <title><![CDATA[Google Says It Still Sends ‘Billions of Clicks’ to Websites]]></title>
      <description><![CDATA[<p>Google’s <a href="https://www.linkedin.com/feed/update/urn:li:activity:7483916000103583744/">Nick Fox</a> on, for some reason, LinkedIn:</p>

<blockquote>
  <p>People also asked if AI in Search would mean that people never click through to websites anymore.</p>
  
  <p>Actually, as we’ve shared before, we continue to send billions of clicks to the web every day through Search. And we’ve designed our AI features in Search to connect people to websites. In fact, we’re now sending billions of clicks to websites every week through AI features in Search alone – and we’re just getting started.</p>
</blockquote>

<p>The specific effects of Google’s A.I. search features is <a href="https://pxlnv.com/blog/unknown-effect-of-google-ai-overviews/">made murkier</a> by all the other features the company has to answer queries immediately instead of sending them to an external website. Nevertheless, the claim that Google still sends “billions of clicks” is just another way of saying Google still has a monopoly on the world’s web searches. It is a substantially similar statement to the one Google <a href="https://pxlnv.com/linklog/google-ai-search-response/">issued a year ago</a>, and something the company is only repeating because the <a href="https://www.nytimes.com/2026/07/20/technology/google-ai-open-web.html"><em>New York Times</em> asked for comment</a>.</p>

<p>If only Google had some kind of dashboard or console with information about a website’s search performance.</p>

<p><a href="https://www.seroundtable.com/google-ai-search-billions-clicks-41709.html">Barry Schwartz</a>, <em>Search Engine Roundtable</em>:</p>

<blockquote>
  <p>Google even built out the <a href="https://www.seroundtable.com/google-ai-performance-report-expands-41549.html">AI performance reports</a> in Search Console but excluded clicks. This is because Google is “continuing to work with website owners to understand what insights will be most helpful to inform their strategies” and Google doesn’t think those are clicks.  Nope, because Google does not want us to see our click-through rates from AI Search features compared to traditional search features.</p>
</blockquote>

<p>I am sure this is a mere oversight that Google will be eager to correct.</p>
<p><a href="https://pxlnv.com/linklog/google-billions-of-clicks/" rel="bookmark" title="Permanent link to 'Google Says It Still Sends ‘Billions of Clicks’ to Websites'" class="glyph">⌥ Permalink</a></p>
]]></description>
      <pubDate>Tue, 21 Jul 2026 23:50:37 +0000</pubDate>
      <link>https://pxlnv.com/linklog/google-billions-of-clicks/</link>
      <dc:creator>Pixel Envy</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5316970766</guid>
    </item>
    <item>
      <title><![CDATA[The name of this LP is “Side One.”]]></title>
      <description><![CDATA[
<p class="wp-block-paragraph">On Sunday, I published <a href="https://jeffreyzeldman.bandcamp.com/album/side-one-2" target="_blank" rel="noopener">Side One of my album on Bandcamp</a>. The name of the album is also “Side One.” It is instrumental music with roots in Soundtrack, hip-hop, funk, jazz, 19th Century Romantic classical, ambient, electronic, and art rock. You might wonder if those divergent genres belong together, but the proof is in the listening.<br><br>For complicated reasons, I haven’t composed, produced, and released music since the late 1980s, back in the multi-track reel-to-reel and early MIDI days! </p>



<p class="wp-block-paragraph">A few months back, a friend bought me Apple’s <a href="https://www.apple.com/logic-pro/">Digital Audio Workstation</a>. I’m teaching myself this entirely new (to me) way of producing music by <em>creating</em> music—like I taught myself HTML and CSS by making websites.</p>



<p class="wp-block-paragraph">It feels like there is no end to the music that has built up inside me over the past decades when I silenced this part of myself. I am thrilled to be creating and sharing music again.</p>



<p class="wp-block-paragraph">I have no commercial expectations for these tracks; I just want people who <em>might</em> like my music to be able to <a href="https://jeffreyzeldman.bandcamp.com/album/side-one-2">give it a listen</a>. If you object to Bandcamp, you can also listen  <a href="https://sonica.music/8yxbzc5igk8j">on Sonica</a>, although the mixes there are slightly different. </p>



<p class="wp-block-paragraph">(I published to Sonica one track at a time; once I had sufficient material to sequence into a proper LP-style side, I remastered the tracks—in some cases also adding parts, and in other cases, taking them away. BTW, if you create music and are looking for a place to share with other creators, <a href="https://sonica.music/">Sonica</a> is a great place for it.)</p>



<p class="wp-block-paragraph">Sonica is also where the curious can hear <a href="https://sonica.music/rsnaz3g9aifc">some of the music I created in my studio during the late 1980s</a>, as well as <a href="https://sonica.music/rj2gdq3s2s38">some initial rough drafts</a> of the newer music that has now found its way into Side One of Side One. <br><br>Side Two of Side One will be out as soon as I finish creating it. Meantime, grab a pair of headphones and <a href="https://jeffreyzeldman.bandcamp.com/album/side-one-2">enjoy Side One of Side One</a>.</p>
<p>The post <a href="https://zeldman.com/2026/07/21/the-name-of-this-lp-is-side-one/">The name of this LP is “Side One.”</a> appeared first on <a href="https://zeldman.com">Jeffrey Zeldman Presents</a>.</p>
]]></description>
      <pubDate>Tue, 21 Jul 2026 14:57:02 +0000</pubDate>
      <link>https://zeldman.com/2026/07/21/the-name-of-this-lp-is-side-one/</link>
      <dc:creator>Jeffrey Zeldman Presents</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5316286067</guid>
    </item>
    <item>
      <title><![CDATA[Copying HTML Tables as Text, Markdown, and CSV]]></title>
      <description><![CDATA[
				
                        <p>Earlier today and in a non web-related conversation, I saw someone talk about copying tabular data into different formats, like plain text and CSV. It got me thinking - this could be a useful feature for web sites and surely something pretty trivial to do in JavaScript. So I whipped up a quick demo.</p>
<p>First, I added a table of course:</p>
<pre><code class="language-html">&lt;table id="table1"&gt;
	&lt;thead&gt;
		&lt;tr&gt;
			&lt;th&gt;Name&lt;/th&gt;
			&lt;th&gt;DOB&lt;/th&gt;
			&lt;th&gt;Gender&lt;/th&gt;
			&lt;th&gt;Breed&lt;/th&gt;
		&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
	&lt;tr&gt;&lt;td&gt;Whiskers&lt;/td&gt;&lt;td&gt;2019-03-14&lt;/td&gt;&lt;td&gt;Male&lt;/td&gt;&lt;td&gt;Maine Coon&lt;/td&gt;&lt;/tr&gt;
	&lt;tr&gt;&lt;td&gt;Luna&lt;/td&gt;&lt;td&gt;2020-07-22&lt;/td&gt;&lt;td&gt;Female&lt;/td&gt;&lt;td&gt;Siamese&lt;/td&gt;&lt;/tr&gt;
	&lt;!-- bunch more rows --&gt;
	&lt;tr&gt;&lt;td&gt;Nala&lt;/td&gt;&lt;td&gt;2022-04-18&lt;/td&gt;&lt;td&gt;Female&lt;/td&gt;&lt;td&gt;Persian&lt;/td&gt;&lt;/tr&gt;
	&lt;tr&gt;&lt;td&gt;Molly&lt;/td&gt;&lt;td&gt;2022-01-11&lt;/td&gt;&lt;td&gt;Female&lt;/td&gt;&lt;td&gt;LaPerm&lt;/td&gt;&lt;/tr&gt;				
	&lt;/tbody&gt;
&lt;/table&gt;
</code></pre>
<p>Alright, so to enable this feature, I thought data attributes might be nice. This would let you use it on anything, a button, a link, image, etc. The data attribute would be responsible for pointing to the table (via an id) and specifying a format. Here's an example:</p>
<pre><code class="language-html">&lt;!-- data-copy defaults to text --&gt;
&lt;a href="" data-table="table1"&gt;Copy as Text&lt;/a&gt; ~ 
&lt;a href="" data-table="table1" data-copy="md"&gt;Copy as Markdown&lt;/a&gt; ~
&lt;a href="" data-table="table1" data-copy="csv"&gt;Copy as CSV&lt;/a&gt;
</code></pre>
<p>Now all I need to do is wire it up to code that looks for <code>data-table</code> and adds the appropriate handlers. The first part is trivial:</p>
<pre><code class="language-js">const links = document.querySelectorAll('*[data-table]');
</code></pre>
<p>Note that while I named the variable <code>links</code>, you can still use this with buttons or images.</p>
<p>I loop over each match and first get the table referenced, throwing an error in console if it doesn't exist:</p>
<pre><code class="language-js">links.forEach(l =&gt; {
	let table = document.querySelector(`#${l.dataset.table}`);
	if(!table) {
		console.error(`Unable to connect to table with id ${l.dataset.table}`);
		return;
	}
</code></pre>
<p>Next, I check the type, defaulting to text:</p>
<pre><code class="language-js">let type = l.dataset.copy || 'text';
</code></pre>
<p>The final bit is to assign the click handler:</p>
<pre><code class="language-js">l.addEventListener('click', async e =&gt; {
	e.preventDefault();
	console.log(`about to copy table ${table.id} to ${type}`);
	let data = getRawData(table);
	let formattedData = formatData(data, type);
	await navigator.clipboard.writeText(formattedData);
});
</code></pre>
<p>For each, I get the raw data from the HTML table, convert it to the right format, and then write to the clipboard. (Check out my <a href="https://master.dev/blog/writing-to-the-clipboard-in-javascript/">article</a> on working with the clipboard in JavaScript.)</p>
<p><code>getRawData</code> just iterates through the table's DOM creating a 2D array:</p>
<pre><code class="language-js">const getRawData = t =&gt; {
	let data = [];
	t.querySelectorAll('tr').forEach(r =&gt; {
		let row = [];
		r.querySelectorAll('th, td').forEach(d =&gt; {
			row.push(d.innerText);
		});
		data.push(row);
	});
	return data;
}
</code></pre>
<p><code>formatData</code> is a bit more complex and I used AI to help me with each of the main sections. I added support for plain text, Markdown, and CSV:</p>
<pre><code class="language-js">const formatData = (data,type) =&gt; {
	let numCols, colWidths, lines, buildRow, buildDivider;
	
	switch(type) {
		case "text": 

			numCols = Math.max(...data.map(r =&gt; r.length));
			// Compute max width for each column
			colWidths = Array(numCols).fill(0);
			for (const row of data) {
			for (let i = 0; i &lt; numCols; i++) {
				const cell = row[i] !== undefined ? String(row[i]) : '';
				colWidths[i] = Math.max(colWidths[i], cell.length);
			}
			}
		
			buildDivider = () =&gt;
			'+' + colWidths.map(w =&gt; '-'.repeat(w + 2)).join('+') + '+';
		
			buildRow = (row) =&gt;
			'|' + colWidths.map((w, i) =&gt; {
				const cell = row[i] !== undefined ? String(row[i]) : '';
				return ' ' + cell.padEnd(w) + ' ';
			}).join('|') + '|';
		
			lines = [];
			lines.push(buildDivider());
			lines.push(buildRow(data[0])); // treat first row as header
			lines.push(buildDivider());
			for (let i = 1; i &lt; data.length; i++) {
			lines.push(buildRow(data[i]));
			}
			lines.push(buildDivider());
		
			return lines.join('\n');

		case "md":
		case "markdown": 
			numCols = Math.max(...data.map(r =&gt; r.length));
			
			buildRow = (row) =&gt;
				'| ' + Array.from({ length: numCols }, (_, i) =&gt;
					row[i] !== undefined ? String(row[i]) : ''
				).join(' | ') + ' |';
		
			lines = [];
			lines.push(buildRow(data[0])); // header
			lines.push('| ' + Array(numCols).fill('---').join(' | ') + ' |');
			for (let i = 1; i &lt; data.length; i++) {
				lines.push(buildRow(data[i]));
			}
		
			return lines.join('\n');

		case "csv": 
			const escapeCell = (value) =&gt; {
			const str = value !== undefined &amp;&amp; value !== null ? String(value) : '';
			// Quote if it contains comma, quote, or newline
			if (/[",\n\r]/.test(str)) {
				return '"' + str.replace(/"/g, '""') + '"';
			}
			return str;
			};
		
			return data.map(row =&gt; row.map(escapeCell).join(',')).join('\n');
			
	}
}
</code></pre>
<p>With my test table (and I'm stripping out a bit here for brevity), here's the plain text version:</p>
<pre><code>+----------+------------+--------+----------------------+
| Name     | DOB        | Gender | Breed                |
+----------+------------+--------+----------------------+
| Whiskers | 2019-03-14 | Male   | Maine Coon           |
| Luna     | 2020-07-22 | Female | Siamese              |
| Oliver   | 2018-11-05 | Male   | British Shorthair    |
| Max      | 2019-06-25 | Male   | Sphynx               |
| Stella   | 2021-05-04 | Female | American Curl        |
| Rocky    | 2018-09-20 | Male   | Burmese              |
| Molly    | 2022-01-11 | Female | LaPerm               |
+----------+------------+--------+----------------------+
</code></pre>
<p>Here's the Markdown:</p>
<pre><code>| Name | DOB | Gender | Breed |
| --- | --- | --- | --- |
| Whiskers | 2019-03-14 | Male | Maine Coon |
| Luna | 2020-07-22 | Female | Siamese |
| Oliver | 2018-11-05 | Male | British Shorthair |
| Max | 2019-06-25 | Male | Sphynx |
| Stella | 2021-05-04 | Female | American Curl |
| Rocky | 2018-09-20 | Male | Burmese |
| Molly | 2022-01-11 | Female | LaPerm |
</code></pre>
<p>And here's CSV:</p>
<pre><code>Name,DOB,Gender,Breed
Whiskers,2019-03-14,Male,Maine Coon
Luna,2020-07-22,Female,Siamese
Oliver,2018-11-05,Male,British Shorthair
Max,2019-06-25,Male,Sphynx
Stella,2021-05-04,Female,American Curl
Rocky,2018-09-20,Male,Burmese
Molly,2022-01-11,Female,LaPerm
</code></pre>
<p>I wrapped this all up in a function that you could add to your page. After adding it, don't forget to actually add the links/button/etc to enable it.</p>
<p>There's two issues with my code that come to mind, and I'd love people to comment in with their ideas. First, both the text and Markdown assume the first row is a header. In theory that might not always be accurate. I could update the code to see if the first cell in the first row is <code>th</code> versus <code>td</code>, but that kinda feels like overkill.</p>
<p>The second issue that comes to mind is user feedback. Usually these types of things will provide some kind of visual feedback ("Data copied!"). My code does not. I honestly don't quite know how I'd do that. Perhaps a data attribute that would replace the text (assuming it's a link or button) temporarily? I'm not sure.</p>
<p>If you've got ideas, leave me a comment below. Here's the demo:</p>
<p class="codepen" data-height="500" data-pen-title="Copy Table As (2)" data-version="2" data-default-tab="result" data-slug-hash="xbgJOyP" data-user="cfjedimaster" style="height: 500px; 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/editor/cfjedimaster/pen/019f80d2-42ea-7545-83bb-8f112023abce">
  Copy Table As (2)</a> by Raymond Camden (<a href="https://codepen.io/cfjedimaster">@cfjedimaster</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async="" src="https://public.codepenassets.com/embed/index.js"></script>
                        
                
				]]></description>
      <pubDate>Mon, 20 Jul 2026 18:00:00 +0000</pubDate>
      <link>https://www.raymondcamden.com/2026/07/20/copying-html-tables-as-text-markdown-and-csv</link>
      <dc:creator>Raymond Camden</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5315313216</guid>
    </item>
    <item>
      <title><![CDATA[Wildcards In CSS [blog]]]></title>
      <description><![CDATA[
<p>Just a brainfart here, but it's odd that even though we have wildcard selectors throughout CSS, and even partial selectors (I'll explain in a bit) we don't have partial wildcard selectors on top level elements.</p>
<p>I'm not talking about the "star" rule, but what I imagine would be a wildcard on elements selector.</p>
<h2>Existing methods</h2>
<p>If I want to select and style all the <code>.btn-*</code> classes using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Attribute_selectors">attribute selector</a>, I'd do this:</p>
<pre><code class="language-css"><span class="token selector">[class^="btn-" i]</span> <span class="token punctuation">{</span>
  …
<span class="token punctuation">}</span>
</code></pre>
<p>This selector says: find all the elements with the class attribute that starts with "btn-" - case insensitive.</p>
<p>It's rather brittle, because it requires that the HTML class attribute starts exactly with <code>btn-</code>. Though it works, it does tie the HTML closely to the CSS - which I'd argue you'd want to be very sure you need to do.</p>
<p>But it is possible.</p>
<p>Equally, and a lot more robust, is the wildcard that says "anywhere in the attribute", so:</p>
<pre><code class="language-css"><span class="token selector">[class*="btn"]</span> <span class="token punctuation">{</span>
  …
<span class="token punctuation">}</span>
</code></pre>
<p>It's fine, but it's a bit sledgehammery. However, if you want to do the same thing for a web component, for instance, you can't.</p>
<p>If I want to target all the <code>rem-xxx</code> custom components using <code>rem-* { … }</code>, I can't.</p>
<p>Obviously I'm not the first to think of this, and <em>of course</em> the venerable <a href="https://lea.verou.me/">Lea Verou</a> has thought about this and <a href="https://github.com/w3c/csswg-drafts/issues/10001">filed the issue</a> over two years ago, though there doesn't seem to be much interest in supporting it.</p>
<p>The reason it crossed my mind is that I was thinking about how you would initialise web components on a page, and currently it requires searching every single element in the DOM, rather than targetting to the nodes you want ahead of time.</p>
<p>From this <a href="https://developer.mozilla.org/en-US/blog/mdn-front-end-deep-dive/">MDN article</a> they included a code snippet that (I thought) was a clever way to initialise the components:</p>
<pre><code class="language-js"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> element <span class="token keyword">of</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"*"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> tag <span class="token operator">=</span> element<span class="token punctuation">.</span>tagName<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>tag<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">"mdn-"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> component <span class="token operator">=</span> tag<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">"mdn-"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">../components/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>component<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/element.js</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>But really, it should be:</p>
<pre><code class="language-js"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> element <span class="token keyword">of</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">"mdn-*"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> component <span class="token operator">=</span> element<span class="token punctuation">.</span>tagName<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token string">"mdn-"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">../components/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>component<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/element.js</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>This feels like a relatively cheap change to add support for wildcards, and gives the <code>querySelectorAll</code> a nice little boost, along with the benefits Lea already outlined in the github issue she raised.</p>
<h2>Would it be valid?</h2>
<p>I swear I found the documentation that read that the <code>*</code> asterisk was reserved for the universal selector (i.e. match all things), but I can't find it any more.</p>
<p>However, the from my memory, the parser only allows an asterisk <em>after</em> a space, to indicate it's a universal selector, so <code>foo*</code> or <code>foo-*</code> would not match in the parser.</p>
<p>Equally, it's allowed in the attribute selector, but <a href="https://www.w3.org/TR/selectors-4/#attribute-substrings">only when followed</a> by <code>=</code>, but attribute selector already has wildcard support. But maybe this would be extended to cover "glob" like support, so I could do <code>.foo*bar</code> to match <code>.foo-dont-do-it-bar</code>. Again, the current parser doesn't allow for this, so that means it's available for use.</p>
<p><em>Originally published on <a href="https://remysharp.com/2026/07/11/wildcards-in-css">Remy Sharp's b:log</a></em></p>]]></description>
      <pubDate>Sat, 11 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://remysharp.com/2026/07/11/wildcards-in-css</link>
      <dc:creator>remy sharp&#39;s b:log</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5303246900</guid>
    </item>
    <item>
      <title><![CDATA[Image to text]]></title>
      <description><![CDATA[

<p>At <a href="https://the-web-you-want.org/"><cite>The Web You Want</cite></a> event in Amsterdam last month, Léonie gave an absolutely fantastic talk. You can now read the whole thing: <a href="https://tink.uk/accessibility-is-resistance/"><cite>Accessibility is resistance!</cite></a></p>

<p>She makes the very good point that generative tools are a necessary accessibility recourse when humans have failed to do their job properly.</p>

<p>Images without <code>alt</code> text? Somebody messed up. But this is damage that can be routed around with large language models.</p>

<p>So if you—like me—don’t like the idea of people using extractive generative tools made by the worst kind of people, pay heed:</p>

<blockquote>
  <p>If you want to resist AI, make accessibility part of everything you do, every decision you make, every product you design and build. Remove the need for people to use AI to compensate, and remember, accessibility is resistance.</p>
</blockquote>

<p>Also last month, I wrote about <a href="https://adactio.com/journal/22613">enhancing images from a carousel to masonry</a> using CSS Grid Lanes. Here’s the context:</p>

<blockquote>
  <p>Over on <a href="https://thesession.org/">The Session</a>, I added a little enhancement to the <a href="https://thesession.org/events">events</a> and <a href="https://thesession.org/sessions">sessions</a> listings recently. I make a call to the Google Places API to see if I can find a match for the venue, and if I do, pull in some photos.</p>

  <p>Sidenote: right now there’s a major issue with this. None of the photos come with text descriptions. This is something I need to fix, and I’ve got some ideas on how to do that.</p>
</blockquote>

<p>I was one of those people with a website that doesn’t have <code>alt</code> text for images. I was one of those people not doing their job.</p>

<p>The first fix I put in place was very much in keeping with the ethos of <a href="https://thesession.org/">The Session</a>. I added a form so that any member of the site can edit the <code>alt</code> text of the images in the carousel/masonry layout.</p>

<p>The Session is quite WikiPedia-like. Anyone can edit the track listings in the discography. Anyone can edit the details of any tune. Anyone can edit the details of any session or event. So it made sense to add one more thing that anyone can edit.</p>

<p>With that functionality in place, <a href="https://thesession.org/discussions/51144">I put out the call</a>:</p>

<blockquote>
  <p>If everyone were to take just one or two sessions or events and add alt text to the photos, it would really, really help make the site more accessible.</p>
</blockquote>

<p>The community went to work and did a pretty good job.</p>

<p>But there are a <em>lot</em> images. I’d like to have a least some kind of <code>alt</code> text for all of them, even if it’s just a placeholder until someone gets around to adding something better.</p>

<p>I was going to have to use generative “AI”, wasn’t I?</p>

<p>Holding my nose, I investigated some alt-text-as-a-service APIs out there. I signed up for some trials and kicked the tyres. They were rubbish. I suspect most of them were vibe-coded.</p>

<p>The more I thought about it, the more I realised that I probably didn’t need the latest and greatest energy-guzzling large langauge models. Some good old-fashioned machine learning could do the trick here. I’m aiming to do a relatively straightforward transformation—image to text.</p>

<p>Check out <a href="https://cocodataset.org">the COCO project</a>: Common Objects in Context. It’s a few years old now, but it’s basically a dataset that’s been trained on images.</p>

<p>Ah, but <a href="https://aiwiki.ai/wiki/coco_dataset">where did the training data come from</a>?</p>

<blockquote>
  <p>All images in COCO were sourced from Flickr, a photo-sharing platform where users upload images under various Creative Commons licenses.</p>
</blockquote>

<p>Works for me!</p>

<p>I found <a href="https://huggingface.co/microsoft/git-base-coco">a model on Hugging Face</a> that was created by Microsoft from the COCO data. Confusingly the model is called GIT. It’s nothing to do with version control. It stands for GenerativeImage2Text.</p>

<p>I downloaded the model and set to work creating a script to trawl through all those images and generate <code>alt</code> text if the image doesn’t already have one.</p>

<p>I should mention: the way that I store the <code>alt</code> text is not in a database, but in the file system. Every image has its own folder. Inside that folder there can also be a file called <code>alt.txt</code>.</p>

<p>So the script needed to loop through lots and lots of folders, generate <code>alt</code> text where needed, and save it into a text file in the same folder.</p>

<p>Python is the go-to language for working with models like GIT. But Python is not my strong suit, to put it mildly. And this was going to be a throwaway script that I was only going to use once. So I held my nose again and turned on Copilot in VSCode (I usually keep it switched off).</p>

<p>With the right instructions I was able to get a script that—crucially—I could read and understand. Then I switched off Copilot and had a shower to try to wash the shame away.</p>

<p>Long story short, the script worked.</p>

<p>The results are mostly fine. The <code>alt</code> text is functional. Sometimes it’s wrong, but not disastrously so. A human-generated image description will always be preferable, so any member of <a href="https://thesession.org/">The Session</a> can over-ride the placeholder <code>alt</code> text.</p>

<p>It took a few hours to loop through all those folders and generate all those text files. My laptop got hot. But doing it locally sure beats paying for tokens from the baddies.</p>

<p><a href="https://social.coop/@scottjenson/116895393513213077">Like Scott says</a>:</p>

<blockquote>
  <p>While the utility is there, the current landscape of these frontier corporate models comes with massive ethical and environmental baggage. This is not a long term solution! </p>

  <p>That’s exactly why my hope is that ethically trained open-weight models will eventually catch up and that in the next couple of years, we’ll have something with this level of capability that can run locally. That feels like a much better place to be with this technology.</p>
</blockquote>


            ]]></description>
      <pubDate>Wed, 15 Jul 2026 20:08:34 +0000</pubDate>
      <link>https://adactio.com/journal/22657</link>
      <dc:creator>Adactio: Journal</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5307845646</guid>
    </item>
    <item>
      <title><![CDATA[Web Platform Wishlist on Vale.Rocks [link]]]></title>
      <description><![CDATA[
<p>I remember the days I was hungry for features on the web…it was…a long time ago! So it's refreshing to read that others still have ideas and want more.</p>
<p>I especially like these two from the post:</p>
<blockquote>
<p>Text overflow truncation from centre…</p>
</blockquote>
<p>And:</p>
<blockquote>
<p>More pseudo-classes. :sticky for if an element is currently sticky, :overflow for if overflowing, etc</p>
</blockquote>
<p>Which falls consistently into the category of "JavaScript is used to do this, it shouldn't need to".</p>
<p><em>Original link: <a href="https://vale.rocks/posts/web-platform-wishlist">vale.rocks/[…]/web-platform-wishlist</a></em></p>
<p><em>Originally published on <a href="https://remysharp.com/links/2026-07-09-221b1dd2">Remy Sharp's b:log</a></em></p>]]></description>
      <pubDate>Thu, 09 Jul 2026 08:41:02 +0000</pubDate>
      <link>https://remysharp.com/links/2026-07-09-221b1dd2</link>
      <dc:creator>remy sharp&#39;s b:log</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5300724730</guid>
    </item>
    <item>
      <title><![CDATA[Typefaces]]></title>
      <description><![CDATA[<p><a href="https://fonts.adobe.com/fonts/kepler"><img src="https://taylor.town/type-kepler.png" alt=""></a>
<a href="https://klim.co.nz/collections/untitled/"><img src="https://taylor.town/type-untitled.png" alt=""></a></p>
<p><a href="https://a2-type.co.uk/search?q=ny"><img src="https://taylor.town/type-ny.png" alt=""></a></p>
<p><a href="https://www.allcapstype.com/typefaces/rhetorik-sans"><img src="https://taylor.town/type-rhetorik.png" alt=""></a></p>
<p><a href="https://www.typeby.com/fonts/arnhem"><img src="https://taylor.town/type-arnhem.png" alt=""></a></p>
<p><a href="https://www.recursive.design"><img src="https://taylor.town/type-recursive.png" alt=""></a></p>
<p><a href="https://a2-type.co.uk/search?q=independent"><img src="https://taylor.town/type-independent.png" alt=""></a></p>
<p><a href="https://abcdinamo.com/typefaces/arizona"><img src="https://taylor.town/type-arizona.png" alt=""></a></p>
<p><a href="https://www.atlasfonts.com/typefaces/novel-serif/"><img src="https://taylor.town/type-novel.png" alt=""></a>
<a href="https://en.wikipedia.org/wiki/Computer_Modern"><img src="https://taylor.town/type-computer.png" alt=""></a></p>
<p><a href="https://commercialtype.com/catalog/terza"><img src="https://taylor.town/type-terza.jpg" alt=""></a></p>
<p><a href="https://www.typeby.com/fonts/quadraat"><img src="https://taylor.town/type-quadraat.png" alt=""></a></p>
<p><a href="https://lettersfromsweden.se/font/gothia-serif-text/"><img src="https://taylor.town/type-gothia.png" alt=""></a></p>
<p><a href="https://rosettatype.com/SkolarPE"><img src="https://taylor.town/type-skolar.png" alt=""></a>
<a href="https://en.wikipedia.org/wiki/Thesis_%28typeface%29"><img src="https://taylor.town/type-thesis.png" alt=""></a></p>
<p><a href="https://www.fairetype.com/collections/sprig"><img src="https://taylor.town/type-sprig.png" alt=""></a></p>]]></description>
      <pubDate>Fri, 10 Jul 2026 00:00:00 +0000</pubDate>
      <link>https://taylor.town/type</link>
      <dc:creator>taylor.town</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5302982801</guid>
    </item>
    <item>
      <title><![CDATA[The Amble One  is perhaps the world’s best-designed golf...]]></title>
      <description><![CDATA[
         	<p><a href="https://driveamble.com/pages/amble-one">The Amble One</a> is perhaps the world’s best-designed golf cart. It’s street-legal, tuned for off-roading, goes 60 miles on a charge, and starts at $25K.
</p>
 

        
        
        
    ]]></description>
      <pubDate>Thu, 09 Jul 2026 18:39:00 +0000</pubDate>
      <link>https://kottke.org/26/07/0049294-the-amble-one</link>
      <dc:creator>kottke.org</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5301479293</guid>
    </item>
    <item>
      <title><![CDATA[Introducing Kyroh]]></title>
      <description><![CDATA[<img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/kyroh-cover-card-1.png" alt="Introducing Kyroh"><p>Greetings Simple Friends,</p><p>Today I'm excited to officially release my latest typeface, <a href="https://simplebits.shop/products/kyroh?ref=simplebits.com" rel="noreferrer"><strong>Kyroh</strong></a>. It's available for sale in the shop, and has also been distributed to all <a href="https://simplebits.com/club" rel="noreferrer">Secret Club</a> members. </p><figure class="kg-card kg-image-card kg-width-wide"><a href="https://simplebits.shop/products/kyroh?ref=simplebits.com"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/kyroh-cover-card.png" class="kg-image" alt="Introducing Kyroh" loading="lazy" width="2000" height="1099" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/kyroh-cover-card.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1000/2026/07/kyroh-cover-card.png 1000w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1600/2026/07/kyroh-cover-card.png 1600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w2400/2026/07/kyroh-cover-card.png 2400w" sizes="(min-width: 1200px) 1200px"></a></figure><p>Kyroh is an all-caps, sans-serif display font inspired by a vintage luggage label from the National Hotel in Cairo, Egypt (found in the amazing book from&nbsp;<a href="https://letterformarchive.org/shop/hotel-retro-vintage-luggage-labels-from-tokyo-to-buenos-aires-330-travel-ephemera-stickers/?srsltid=AfmBOopa2RElERv5qqQI5JLjlJ3QeWreqPZxDZ-lMfIcV5aBaTXUsWWO&amp;ref=simplebits.com" rel="noreferrer">Letterform Archive</a> which is highly recommended).</p><figure class="kg-card kg-image-card"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/IMG_1495--1-.png" class="kg-image" alt="Introducing Kyroh" loading="lazy" width="2000" height="1110" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/IMG_1495--1-.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1000/2026/07/IMG_1495--1-.png 1000w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1600/2026/07/IMG_1495--1-.png 1600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/IMG_1495--1-.png 2149w" sizes="(min-width: 720px) 720px"></figure><p>I riffed off the hand-lettered word “EGYPT” from the label and filled out the rest of the alphabet informed by those five letterforms. I love how wide and chunky the word was lettered, and the simplified G and lowercase-ish Y had me thinking of how the rest of the alphabet might play off of those imperfect, inky, rounded-but-rough shapes.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/kyroh-pyr-sq.png" width="2000" height="2000" loading="lazy" alt="Introducing Kyroh" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/kyroh-pyr-sq.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1000/2026/07/kyroh-pyr-sq.png 1000w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1600/2026/07/kyroh-pyr-sq.png 1600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w2400/2026/07/kyroh-pyr-sq.png 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/kyroh-w-sq.png" width="2000" height="2000" loading="lazy" alt="Introducing Kyroh" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/kyroh-w-sq.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1000/2026/07/kyroh-w-sq.png 1000w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1600/2026/07/kyroh-w-sq.png 1600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w2400/2026/07/kyroh-w-sq.png 2400w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>Kyroh comes in&nbsp;<strong>three weights</strong>: Regular, Medium, and Bold and has our standard extended language support with diacritics and additional glyphs. Plus! A few Egyptian icons based on some of the common hieroglyphics.</p><figure class="kg-card kg-image-card kg-width-wide"><a href="https://simplebits.shop/products/kyroh?ref=simplebits.com"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/kyroh-hotel-cm.png" class="kg-image" alt="Introducing Kyroh" loading="lazy" width="2000" height="1334" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/kyroh-hotel-cm.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1000/2026/07/kyroh-hotel-cm.png 1000w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w1600/2026/07/kyroh-hotel-cm.png 1600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w2400/2026/07/kyroh-hotel-cm.png 2400w" sizes="(min-width: 1200px) 1200px"></a></figure><div class="kg-card kg-button-card kg-align-center"><a href="https://simplebits.shop/products/kyroh?ref=simplebits.com" class="kg-btn kg-btn-accent">GET KYROH</a></div><p>Kyroh is available now,&nbsp;<a href="https://simplebits.shop/products/kyroh?ref=simplebits.com" rel="noreferrer"><strong>individually in our shop</strong></a>&nbsp;or&nbsp;<a href="https://simplebits.com/club" rel="noreferrer"><strong>join our Secret Club™</strong></a>&nbsp;and get ALL the SimpleBits® fonts + other perks for just $60/year.</p><p>Thanks for your support! Enjoy the new font, tag us if you use it, and see you back here on Friday for my next Studio Notes™.</p><p>Cheers,</p><figure class="kg-card kg-image-card"><img src="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/sig-1.png" class="kg-image" alt="Introducing Kyroh" loading="lazy" width="1000" height="77" srcset="https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/size/w600/2026/07/sig-1.png 600w, https://storage.ghost.io/c/e6/db/e6db78f5-f507-465e-97cb-f965b4d12d26/content/images/2026/07/sig-1.png 1000w" sizes="(min-width: 720px) 720px"></figure>]]></description>
      <pubDate>Tue, 07 Jul 2026 16:59:44 +0000</pubDate>
      <link>https://simplebits.com/n/introducing-kyroh/</link>
      <dc:creator>SimpleBits®</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5295983725</guid>
    </item>
    <item>
      <title><![CDATA[Talk: Let’s fix the web’s text size - Josh Tumath]]></title>
      <description><![CDATA[

<p>I saw Josh give this talk at CSS Day. It’s a very useful explanation of the forthcoming <code>text-scale</code> addition to the <code>meta</code> element, and what you can do to prepare your site now.</p>

<p><a href="https://adactio.com/links/22644">adactio.com/links/22644</a></p>

            ]]></description>
      <pubDate>Wed, 08 Jul 2026 14:13:53 +0000</pubDate>
      <link>https://www.joshtumath.uk/posts/2026-07-02-lets-fix-the-webs-text-size/</link>
      <dc:creator>Adactio: Links</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5298224194</guid>
    </item>
    <item>
      <title><![CDATA[John Herrman: The Declawing of OpenClaw]]></title>
      <description><![CDATA[<figure class="episode-image"><img src="https://ftrain.com/art/uploads/aboard-podcast/images/john-herrman-the-declawing-of-openclaw.jpg" alt="John Herrman: The Declawing of OpenClaw"></figure>
<div class="episode-description"><p>OpenClaw sprung up overnight—and seemed to fade just as quickly. What does that suggest about AI more broadly? On this week’s podcast, Paul and Rich are joined in the studio by <em>New York Magazine </em>tech reporter John Herrman, whose long career covering all corners of the industry has, like the rest of his peers, recently shifted to all AI, all the time. They discuss his piece for the magazine on OpenClaw—titled “My Adventures With ‘The AI That Actually Does Things’”—before pulling back the lens to look at AI on a whole. Plus: They take a brief diversion into the story John wrote immediately after his OpenClaw reporting, “The Mystery of 'Human Meat' for Sale on Temu.”</p></div>
<div class="episode-media">
  <p><a href="https://episodes.captivate.fm/episode/ebb09a33-98fe-4135-8cda-46cf7ee02d41.mp3" class="episode-audio">🎧 Listen to Episode</a></p>
</div>
]]></description>
      <pubDate>Tue, 07 Jul 2026 11:00:00 +0000</pubDate>
      <link>https://ftrain.com/aboard-podcast-john-herrman-the-declawing-of-openclaw</link>
      <dc:creator>Ftrain.com</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5297866566</guid>
    </item>
    <item>
      <title><![CDATA[Making a Shuffle Button]]></title>
      <description><![CDATA[<p>I made some updates to <a href="https://notes.jim-nielsen.com">my notes blog</a>, including a change to how my “Shuffle” feature worked.</p>
<p>Figured I’d blog about it.</p>
<h2 id="shuffle-on-a-blog">Shuffle? On a Blog?</h2>
<p>At the time of this writing, I have 974 “notes” that I’ve published. </p>
<p>For fun, I have a “shuffle” button that digs up a random note from the past. I like to press it from time to time and re-encounter some insight from the past.</p>
<p>It’s like going through an old album, pulling out a random photo, and thinking, “Oh yeah, I remember this! Good times.”</p>
<p>Like old photos, there’s also the occasional “that didn’t age so well”. </p>
<p>But I find it fun to randomly dig up old insights from others and continue to be inspired.</p>
<h2 id="how-did-it-work-before">How Did It Work Before?</h2>
<p>Since my site is built and hosted as static files without a runtime server, this feature required JavaScript to work.</p>
<p>Every page had a snippet like this:</p>
<pre><code class="language language-html"><span class="hljs-comment">&lt;!-- In the site navigation --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"js-shuffle"</span>&gt;</span>Shuffle<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Way down at the end of the HTML --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span>
  // All 974 note IDs injected by my SSG
  const noteIds = ['id-1', 'id-2', id-3', 'id-4', '...'];
  document.querySelector("#js-shuffle")
    .addEventListener('click', () =&gt; {
      // randomly grab an item in `noteIds`
      const randomId = '...';
      window.location.href = `/n/${randomId}/`
    })
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Essentially: inject every note ID into every HTML page and, when the shuffle button is clicked, randomly grab one and navigate the user to it.</p>
<p>Not the most elegant thing, but it worked.</p>
<p>The problem was that every time I published a new post, every single page had to be re-uploaded to Netlify because every file’s hash would change and its etag/cache was invalidated.</p>
<p>This made my builds slow. It also made it difficult, from a development perspective, to ensure refactors didn’t result in unexpected changes to output (using <a href="https://weborigami.org/builtins/dev/changes"><code>Dev.changes</code> from my SSG web origami</a>).</p>
<p>So I decided to make a change.</p>
<h3 id="what-about-a-no-js-approach">What About a No-JS Approach?</h3>
<p>Because I love to see if I can make things work without JavaScript, I had the thought to randomly write the <code>href</code> at build time using my SSG, which would result in output like this:</p>
<pre><code class="language language-html"><span class="hljs-comment">&lt;!-- /notes/1 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/notes/3"</span>&gt;</span>Shuffle<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>

<span class="hljs-comment">&lt;!-- /notes/2 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/notes/1"</span>&gt;</span>Shuffle<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>

<span class="hljs-comment">&lt;!-- /notes/3 --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/notes/2"</span>&gt;</span>Shuffle<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
</code></pre>
<p>And every time I re-build my site, just have this logic run on the static site generator so that it’s different <code>href</code> for every page, every time.</p>
<p>Pros:</p>
<ul>
<li>Doesn’t require JavaScript</li>
<li>Doesn’t require a server (request-time logic)</li>
</ul>
<p>Cons:</p>
<ul>
<li>File hashes change across builds (even if there’s no new content or template changes, every HTML page now has a different <code>href</code> for the shuffle link <em>for every build</em>). This makes deployments <em>way</em> slower because Netlify has to redeploy <em>every</em> file on <em>every</em> build. Plus Etags change so caching is basically ineffectual.</li>
</ul>
<p>I decided I didn’t want to do this, so on to JavaScript!</p>
<h2 id="how-does-it-work-now">How Does It Work Now?</h2>
<p>My first thought was to create a single JSON file that contained all my note IDs. Then when the “Shuffle” button gets clicked, I fetch that, grab a random ID, and navigate the user, e.g.</p>
<pre><code class="language language-html"><span class="hljs-comment">&lt;!-- In the site navigation --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"js-shuffle"</span>&gt;</span>Shuffle<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-comment">&lt;!-- Way down at the end of the HTML --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="language-javascript">
  <span class="hljs-keyword">const</span> noteIds = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">"/notes.json"</span>).<span class="hljs-title function_">then</span>(...);
  <span class="hljs-comment">// Handler code for when button is clicked</span>
  <span class="hljs-comment">// grab a note ID &amp; navigate to that location</span>
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>This would work. It localizes the caching issue to a single file, so only one file has to be invalidated/re-uploaded across builds.</p>
<p>But in playing with it a little more, I decided to try something a little more...unconventional.</p>
<p>I’ve written before about having <a href="https://blog.jim-nielsen.com/2025/lots-of-little-html-pages/">lots of little HTML pages</a> and I thought, “Can I put this functionality in a single HTML page rather than a JSON file?”</p>
<p>And what I ended up with was a link, e.g.</p>
<p><code>&lt;a href="/shuffle/"&gt;Shuffle&lt;/a&gt;</code></p>
<p>That when clicked navigates the user to a new page. That page has all the JS logic embedded in it, e.g.</p>
<pre><code class="language language-html"><span class="hljs-comment">&lt;!-- In the site navigation --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Shuffling...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>

<span class="hljs-comment">&lt;!-- In the body of the page --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">noscript</span>&gt;</span>JS is required to use the Shuffle functionality<span class="hljs-tag">&lt;/<span class="hljs-name">nocript</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span>
  // All 974 note IDs injected by my SSG
  const noteIds = ['id-1', 'id-2', id-3', 'id-4', '...'];\

  // A slight delay so user sees "Shuffling..." effect
  setTimeout(() =&gt; {
    // randomly grab an item in `noteIds`
    const randomId = '...';
    window.location.href = `/n/${randomId}/`
  }, 300);
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>There are a few things I like about the experience this implementation provides.</p>
<p>First: shuffle is a <em>route</em>, so I can navigate to it directly without using the GUI, e.g. <a href="https://notes.jim-nielsen.com/shuffle/">notes.jim-nielsen.com/shuffle</a></p>
<p>Second: I handle the UI/X with a slight delay to make it appear like something is happening when you click the button. If you click the button and it immediately jumps to the next, randomized page, it almost seems to happen too fast. Like you’re left with this feeling of “What just happened?”</p>
<p>But in this scenario, it navigates you to the “Shuffle” page, the button you just clicked turns into a spinner + text indicating something is happening, and there’s a slight (intentional) delay before the JS executes and sends you to a randomized note.</p>
<p>I know it’s a bit weird. “Introduce artificial slowness? Are you crazy?” But I like it. It feels like the shuffle feature on an old music player.</p>
<p>I remember one of my CD players had a “Shuffle” feature. When I’d click the button, it would display “Shuffling…” on the little black and white screen and you’d encounter this brief state where (I presume) the lens inside the hardware would move along the physical track to the spot where it would start reading a new, random song from the CD.</p>
<p>The hardware constraints necessitated this kind of an experience, but I always liked it because it felt like the CD player was “thinking” about what track to pick next. This state clearly conveyed to me that my intent to shuffle was received and being followed. I liked that feedback, and it’s exactly what I wanted to do on my notes site (even though it was completely unnecessary).</p>
<p class="image-container"><img src="https://cdn.jim-nielsen.com/blog/2026/shuffle-routes.png" width="1146" height="892" alt="Screenshot of three routes side-by-side indicating a shuffle-like navigation between them." data-og-image="">

</p><p>I like having that brief moment of feedback where it’s very clear that your intention was received and being followed, vs. having it happen so fast you can’t even perceive precisely what happened.</p>
<p>Here’s a video to show it in action:</p>
<p><video controls="" src="https://cdn.jim-nielsen.com/blog/2026/shuffle-recording.mp4" width="301" height="655"></video></p>
<h2 id="why">Why?</h2>
<p>I know that’s a lot of information for something so small — and, arguably, unnecessary.</p>
<p>But I still enjoy writing about how I make decisions when I build things for myself.</p>
<p>Hence this post.</p>

    <hr>
    

    <p>
      Reply via:
      

      <a href="mailto:jimniels%2Bblog@gmail.com?subject=Re:%20blog.jim-nielsen.com/2026/notes-shuffle/">Email</a>
      · <a href="https://mastodon.social/@jimniels">Mastodon</a> ·

      <a href="https://bsky.app/profile/jim-nielsen.com">Bluesky</a>
    </p>

    
  ]]></description>
      <pubDate>Sun, 05 Jul 2026 19:00:00 +0000</pubDate>
      <link>https://blog.jim-nielsen.com/2026/notes-shuffle/</link>
      <dc:creator>Jim Nielsen’s Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5293348481</guid>
    </item>
    <item>
      <title><![CDATA[People and Blogs interviewed me]]></title>
      <description><![CDATA[<p>thanks to Manu for asking me to be the second-to-last blogger in his interview series</p>
 <a href="https://waxy.org/2026/07/people-and-blogs-interviewed-me/">#</a>]]></description>
      <pubDate>Sun, 05 Jul 2026 16:57:05 +0000</pubDate>
      <link>https://manuelmoreale.com/interview/andy-baio</link>
      <dc:creator>Waxy.org</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5292969725</guid>
    </item>
    <item>
      <title><![CDATA[Fantastical 4.1.15 Adds Calendar Mirroring]]></title>
      <description><![CDATA[
<p>Flexibits:</p>

<blockquote>
  <p>Calendar Mirroring allows you to connect two separate calendars
(like work and personal) so that events from one automatically
show up on the other.</p>

<p>The best part? <em>No event information is sent to Flexibits servers
or saved outside of your device.</em></p>

<p>You can choose to show full event details or just block the time
out as a mysterious, professional “Busy”. Your coworkers don’t
need to know you’re getting a root canal, they just need to know
you’re unavailable.</p>
</blockquote>

<p>In Flexibits’s example scenario, the idea is that you have a personal calendar with important events that you want to mirror to your work calendar, to block the times for those events off — and you might just want them marked as “Busy” on your work calendar, rather than revealing the actual details.</p>

<p>I’ve been using this feature in beta for a few months and love it, even though my use case is seemingly simple. For recording episodes of <a href="https://dithering.fm/">Dithering</a>, Ben Thompson and I have a shared Microsoft 365 calendar. (You can guess which of the two of us set that up by that fact.) Fantastical has long had terrific built-in support for Microsoft 365 accounts. So for me, those events have always just shown up in Fantastical. For me.</p>

<p>The problem is, my wife and I share an iCloud calendar, where we put events we want each other to know about. My Dithering recordings have never shown up there. Ben and I record on a pretty regular schedule, but it’s always been a minor irritation that my wife can’t see when I’m booked for Dithering. Fantastical’s new mirroring feature solves this perfectly. I set up a mirror to copy all events from my Dithering calendar to my family calendar, keeping the original event titles rather than obscuring them as “Busy”. (The titles all just say “Dithering”.)</p>

<p>The icing on the cake is Fantastical’s longstanding “Combine identical events” preference setting. Because I have that setting on, I don’t see duplicate “Dithering” events — one from my Dithering calendar, and another from my family calendar. I just see one event for each scheduled recording, with a striped dual-color swatch that indicates that this one event exists on both calendars. It’s just perfect.</p>

<p><strong>One more thing:</strong> Also somewhat recently, <a href="https://flexibits.com/blog/2026/03/feature-spotlight-introducing-the-fantastical-connector-mcp-for-claude/">Fantastical added support for Anthropic’s MCP</a> to integrate your calendaring with Claude Desktop and every other AI agent that <a href="https://daringfireball.net/linked/2026/07/02/safari-mcp">supports the standard</a>. David Sparks made a <a href="https://www.youtube.com/watch?app=desktop&amp;v=BcO0R-DfOg4">short demo video that shows it off</a>. I don’t really use Claude so this didn’t hit for me personally, but it seems cool enough that it made me at least consider, for a moment, switching from ChatGPT to Claude. Then I remembered <a href="https://daringfireball.net/2026/07/claudes_criminally_bad_mac_app_is_an_inside_job">what the Claude app is like</a>.</p>

<div>
<a title="Permanent link to ‘Fantastical 4.1.15 Adds Calendar Mirroring’" href="https://daringfireball.net/linked/2026/07/04/fantastical-calendar-mirroring">&nbsp;★&nbsp;</a>
</div>

	]]></description>
      <pubDate>Sat, 04 Jul 2026 16:57:22 +0000</pubDate>
      <link>https://flexibits.com/blog/2026/06/double-booked-never-heard-of-it-meet-calendar-mirroring-in-fantastical/</link>
      <dc:creator>Daring Fireball</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5292244871</guid>
    </item>
    <item>
      <title><![CDATA[Introducing the Safari MCP server for web developers]]></title>
      <description><![CDATA[<p><strong>Update: </strong>In <a href="https://developer.apple.com/safari/resources/">Safari 27 beta</a> and <a href="https://developer.apple.com/safari/technology-preview/">Safari Technology Preview 247</a>, we’re introducing the Safari MCP server — a Model Context Protocol server for web developers that makes your web development and debugging workflow faster and more powerful. We know agents are increasingly integral to the coding process and the Safari MCP server gives your agent the ability to know how your code actually renders in the browser by connecting it to a Safari browser window.</p>
<p>Any MCP-compatible client can connect to the Safari MCP server. By connecting your agent to a Safari browser window, your agent can emulate what your users experience, giving it the information it needs to debug more autonomously, like access to the DOM, network requests, screenshots, and console output.</p>
<p>It speeds up your debugging process and lets you stay in the comfort of your terminal, which means fewer rounds of hopping windows and typing prompts to debug your code.</p>
<h2>The use cases</h2>
<p>If you build for the web, then you know about the debugging dance. It usually goes something like this:</p>
<p>You see something wrong with your site in the browser. You open the console to hunt it down. You click into the styles tab. You see what’s broken. You go back to your code to fix it. Or maybe you take a screenshot, detail the problem to your agent, and let it do the fixing for you. Hopefully it gets it right, the bug is fixed, and you can move on.</p>
<p>But when it isn’t fixed, you go through the workflow again — Browser. Prompt. Agent.</p>
<p>And again and again, until you finally squash the bug.</p>
<p>Regardless of the browser or tools you use, the debugging workflow is a lot of clicks, tools, and window hopping to make a single fix, but it doesn’t have to be that way. If you’re already using agents in your development workflow, the Safari MCP server makes your debugging faster and more efficient.</p>
<p>The Safari MCP server enables your agent to do more debugging and troubleshooting on its own. Here are just a few examples of what it can help with:</p>
<p><strong>Web development in Safari</strong>. The next time you develop in Safari, you’ll benefit from an upgraded workflow. Your agent already helps you with your code, now it can do even more by checking out how your code actually renders in Safari.</p>
<p><strong>Improve compatibility with Safari.</strong> Testing in just one browser means missing potential bugs in another, giving those users a subpar experience. With the Safari MCP server, your agent can open your site in Safari, inspect computed styles, check layout, and compare it against what you expect without switching windows.</p>
<p><strong>Analyze performance.</strong> See what parts of your site are slowing things down. The Safari MCP server lets your agent evaluate JavaScript on the page to surface performance metrics, like navigation timing and resource load times, so it can pinpoint what’s slowing your site down and work on the right fix.</p>
<p><strong>Check for accessibility.</strong> The Safari MCP server lets your agent check for common accessibility issues like missing labels, improper ARIA attributes, and poor contrast, so you can catch problems that impact your users.</p>
<p><strong>Verify any user state.</strong> Know that the page is working and looking as it should. Your agent can check the state of the form, query an element using a selector, confirm specific interactions, show different states of a checkout flow, and more. Spend less time on these manual checks and empower the agent to do it for you.</p>
<p>These are just a few of the use cases. However you decide to implement it, the Safari MCP server helps your agent do more for you and reduce all the back and forth that web development often requires. An easier workflow means more bugs squashed, happier users, and a better product.</p>
<h2>The tools</h2>
<p>Here are the available tools and what they do:</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="min-width: 21ch;">browser_console_messages</td>
<td>Return buffered console logs for the current or specified tab</td>
</tr>
<tr>
<td style="min-width: 21ch;">browser_dialogs</td>
<td>List and respond to browser dialogs (accept, dismiss, or input text for JS prompts)</td>
</tr>
<tr>
<td style="min-width: 21ch;">close_tab</td>
<td>Close a browser tab by its handle</td>
</tr>
<tr>
<td style="min-width: 21ch;">create_tab</td>
<td>Create a new browser tab, optionally loading a URL</td>
</tr>
<tr>
<td style="min-width: 21ch;">evaluate_javascript</td>
<td>Execute JavaScript code within the page and return the result</td>
</tr>
<tr>
<td style="min-width: 21ch;">get_network_request</td>
<td>Get full detail for a single recorded network request (headers, body, timing)</td>
</tr>
<tr>
<td style="min-width: 21ch;">get_page_content</td>
<td>Extract text content of a page in various formats (markdown, HTML, JSON, etc.)</td>
</tr>
<tr>
<td style="min-width: 21ch;">list_network_requests</td>
<td>List network request summaries (URL, method, status, timing) for the current tab</td>
</tr>
<tr>
<td style="min-width: 21ch;">list_tabs</td>
<td>List all open browser tabs with their handles and URLs</td>
</tr>
<tr>
<td style="min-width: 21ch;">navigate_to_url</td>
<td>Navigate to a URL and return the loaded page’s content</td>
</tr>
<tr>
<td style="min-width: 21ch;">page_info</td>
<td>Get info about the current page: URL, title, and loading state</td>
</tr>
<tr>
<td style="min-width: 21ch;">page_interactions</td>
<td>Perform DOM interactions in sequence: click, type, scroll, hover, keyPress, etc.</td>
</tr>
<tr>
<td style="min-width: 21ch;">screenshot</td>
<td>Capture a screenshot of the current page as a PNG</td>
</tr>
<tr>
<td style="min-width: 21ch;">set_emulated_media</td>
<td>Emulate a CSS media type (e.g. “print”) for responsive-design testing</td>
</tr>
<tr>
<td style="min-width: 21ch;">set_viewport_size</td>
<td>Set the browser viewport size in CSS pixels</td>
</tr>
<tr>
<td style="min-width: 21ch;">switch_tab</td>
<td>Switch to a different browser tab by its handle</td>
</tr>
<tr>
<td style="min-width: 21ch;">wait_for_navigation</td>
<td>Wait for the current page to finish loading; returns final URL and title</td>
</tr>
</tbody>
</table>
<p>With the Safari MCP server, you no longer have to write the perfect prompt, carefully describing to your agent what you’re experiencing in the browser. You can give your agent the ability to find out for itself.</p>
<h2>How to get started</h2>
<h3>Safari 27 beta</h3>
<p>First, you’ll need to install <a href="https://developer.apple.com/safari/resources/">Safari 27 beta</a>. Once installed, make sure to enable web developer features and remote automation. To enable features for web developers choose Safari &gt; Settings &gt; Advanced &gt; check the Show features for web developers checkbox. Then go to Safari &gt; Settings &gt; Developer &gt; check “Allow remote automation and external agents.”</p>
<p>If you’re using Claude, you can use the following command in your terminal:</p>
<pre><code>claude mcp add safari-mcp -- "/usr/bin/safaridriver" --mcp
</code></pre>
<p>If you’re using Codex, you can use the following command in your terminal:</p>
<pre><code>codex mcp add safari-mcp -- "/usr/bin/safaridriver" --mcp
</code></pre>
<p>For other agents, you can put the following in your mcp.json or config.json file.</p>
<pre><code class="json">{
  <span class="string">"mcpServers"</span><span class="operator">:</span> {
    <span class="string">"safari-mcp"</span><span class="operator">:</span> {
      <span class="string">"command"</span><span class="operator">:</span> <span class="string">"/usr/bin/safaridriver"</span>,
      <span class="string">"args"</span><span class="operator">:</span> [<span class="string">"--mcp"</span>]
    }
  }
}
</code></pre>
<h3>Safari Technology Preview</h3>
<p>First, you’ll need to install <a href="https://developer.apple.com/safari/technology-preview/">Safari Technology Preview</a>. Once installed, make sure you enable Safari Settings &gt; Advanced &gt; Show features for web developers. Then go to Safari Settings &gt; Developer &gt; Enable remote automation and external agents.</p>
<p>If you’re using Claude, you can use the following command in your terminal:</p>
<pre><code>claude mcp add safari-mcp-stp -- "/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver" --mcp
</code></pre>
<p>If you’re using Codex, you can use the following command in your terminal:</p>
<pre><code>codex mcp add safari-mcp-stp -- "/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver" --mcp
</code></pre>
<p>For other agents, you can put the following in your <code>mcp.json</code> or <code>config.json</code> :</p>
<pre><code class="json"><span class="string">"safari-mcp-stp"</span><span class="operator">:</span> {
  <span class="string">"command"</span><span class="operator">:</span> <span class="string">"/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver"</span>,
  <span class="string">"args"</span><span class="operator">:</span> [<span class="string">"--mcp"</span>] 
}
</code></pre>
<p>Note that while the above code names the server <code>safari-mcp-stp</code>, you can name it whatever you like, even just <code>safari</code>. Once you have the Safari MCP server installed, try one of the following prompts:</p>
<pre><code>Find bugs on my site in Safari
</code></pre>
<pre><code>How accessible is my site in Safari?
</code></pre>
<pre><code>See how my website performs in Safari
</code></pre>
<p>While each agent works a little differently, it shouldn’t need to be told to use the Safari MCP server explicitly — it’ll figure it out on its own. Simple prompts like the ones above are enough to kickstart the MCP.</p>
<p>Here’s how a conversation with an agent using the Safari MCP server might go:</p>
<div style="background:#1a1a1a; color:#e0e0e0; border-radius:8px; padding:20px; font-family:'Courier New', monospace; font-size:0.9em; width:100%; box-sizing:border-box; line-height:1.6;">
<div style="margin-bottom:1em; text-indent:0;"><span style="color:#0a84ff; font-weight:bold;">You:</span> Got a bug report — something’s broken in Safari on the flight page. Can you figure out what’s going on?</div>
<div style="margin-bottom:1em; text-indent:0;"><span style="color:#aaa; font-weight:bold;">Agent:</span> Sure, let me take a look.</div>
<div style="margin-bottom:1em; text-indent:0;"><span style="color:#aaa; font-weight:bold;">Agent:</span> I found two distinct bugs on the flight page in Safari. Want me to fix them both?</div>
<div style="margin-bottom:1em; text-indent:0;"><span style="color:#0a84ff; font-weight:bold;">You:</span> Yes. Anything else here that might be causing problems for Safari users?</div>
<div style="text-indent:0;"><span style="color:#aaa; font-weight:bold;">Agent:</span> Yes, two more things worth flagging. The results reveal animation is silently broken and the API server startup log is wrong. Should I tackle both as well?</div>
<p></p></div>
<p>&nbsp;</p>
<p>All you need is an initial request to get started, and with the help of the Safari MCP server, your agent can take it from there.</p>
<p>The Safari MCP server runs entirely on your local machine and makes no network calls of its own. It also does not have access to your personal information in Safari (e.g. AutoFill or other browser activity). When it captures page content, screenshots, or console logs, that data goes directly to the agent you’re running — not to Apple. What happens to that data from there depends on the agent and model you’re using. As with any agent you give access to your browser, only use ones you trust.</p>
<h2>Why we built this</h2>
<p>There are many ways to build for the web, both with and without AI. If AI is a part of your workflow, we think this tool will help make it even more productive. And if it isn’t, that’s OK too.</p>
<p>By creating this resource, we hope to make it easier than ever to test and debug in Safari by helping your agent understand how things look and work in the browser.</p>
<p>If you end up giving it a try or if this is your first time using an MCP server, let us know what you think.</p>
<p>Find us online: Saron Yitbarek on <a href="https://bsky.app/profile/saron.bsky.social">BlueSky</a>, Jen Simmons on <a href="https://bsky.app/profile/jensimmons.bsky.social">Bluesky</a> / <a href="https://front-end.social/@jensimmons">Mastodon</a>, and Jon Davis on <a href="https://bsky.app/profile/jondavis.bsky.social">Bluesky</a> / <a href="https://mastodon.social/@jondavis">Mastodon</a>. If you run into any issues, file a <a href="https://bugs.webkit.org/">WebKit bug report</a>. Filing issues really does make a difference.</p>
]]></description>
      <pubDate>Wed, 01 Jul 2026 21:28:16 +0000</pubDate>
      <link>https://webkit.org/blog/18136/introducing-the-safari-mcp-server-for-web-developers/</link>
      <dc:creator>Blog – WebKit</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5289415749</guid>
    </item>
    <item>
      <title><![CDATA[I love  the aesthetic of Fresco , a video game where you...]]></title>
      <description><![CDATA[
         	<p>I love <a href="https://www.youtube.com/watch?v=fc51terNNNw">the aesthetic of Fresco</a>, a video game where you play as a character embedded into Egyptian wall paintings. In many ways, these <a href="https://www.metmuseum.org/met-publications/egyptian-wall-paintings-the-metropolitan-museum-of-arts-collection-of-facsimiles">wall paintings</a> are ancient precursors to side-scrolling games.
</p>
 

        
        
        
    ]]></description>
      <pubDate>Wed, 01 Jul 2026 17:37:00 +0000</pubDate>
      <link>https://kottke.org/26/07/0049230-the-aesthetic-of-fresco</link>
      <dc:creator>kottke.org</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5289228045</guid>
    </item>
    <item>
      <title><![CDATA[I Mostly Stopped Typing]]></title>
      <description><![CDATA[<p>I built the dictation app <em>I</em> wanted. It's called <a href="https://tonguetype.app/">TongueType</a>, and my daughter did the voice over for the video. (Family business.)</p>
<p>It hasn't gotten much traction yet, and I think I know why: dictation has been bad for so long that most people stopped paying attention. I don't blame them. But I don't think most people realize how good local models have gotten. The thing that was flaky and frustrating five years ago is genuinely good now, and it runs entirely on your Mac.</p>
<p>Downloads are low. But the conversion rate is great. The people who actually try it tend to stick around, which tells me the problem isn't the app, it's getting someone to give dictation one more honest chance.</p>
<h2 id="the-real-hurdle-is-the-habit" tabindex="-1"><a class="direct-link" href="https://www.abeautifulsite.net/posts/i-mostly-stopped-typing/#the-real-hurdle-is-the-habit">The real hurdle is the habit</a></h2>
<p>The hard part isn't accuracy. It's that talking instead of typing is a new habit, and new habits are awkward before they're automatic. For the first week it feels strange. You catch yourself reaching for the keyboard out of muscle memory.</p>
<p>Then one day it clicks, and you realize how slow typing was making you for certain things.</p>
<p>I still write code by hand. That's thinking, not transcribing, and I want my fingers on it (plus saying HTML tags and attributes out loud seems counterintuitive 😂). But for almost everything else, I talk.</p>
<ul>
<li>I prompt LLMs</li>
<li>I send emails</li>
<li>I reply on Slack</li>
<li>I write commit messages</li>
<li>I do most other text with my voice</li>
</ul>
<p>The common thread is that the thinking is already done and the only thing left is getting the words out. That turns out to be a surprising amount of my day.</p>
<h2 id="my-desk-setup" tabindex="-1"><a class="direct-link" href="https://www.abeautifulsite.net/posts/i-mostly-stopped-typing/#my-desk-setup">My desk setup</a></h2>
<p>On the go, the MacBook's built-in mic works just fine. You don't need fancy hardware to get good results. But when I'm at my desk, my laptop is docked, so I pair TongueType with a <a href="https://tulamics.com/">Tula&nbsp;mic</a>. It's small, portable, sounds great, and looks the part! (<a href="https://www.unibrands.co/collections/kuru-toga">Kuru&nbsp;Toga</a> mechanical pencil positioned for size comparison.)</p>
<p><img src="https://www.abeautifulsite.net/images/tula-mic.webp" alt="A Tula microphone on a stand in front of a monitor, with a blue pen in the foreground for scale"></p>
<p><em>One tip:</em> use a wired mic if you can. Bluetooth adds latency, and latency is the enemy of a tool you reach for dozens of times a day. A wired connection makes the whole thing feel instant.</p>
<p>Honest caveat: it's probably not a great look in a co-working space. 😂</p>
<h2 id="%22why-is-this-better-than-macos-dictation%3F%22" tabindex="-1"><a class="direct-link" href="https://www.abeautifulsite.net/posts/i-mostly-stopped-typing/#%22why-is-this-better-than-macos-dictation%3F%22">"Why is this better than macOS dictation?"</a></h2>
<p>More accurate. Less flaky. More customizable. More fun.</p>
<p>I could list the features, but the honest answer is the same one I'd give for most things: <a href="https://tonguetype.app/">just try it</a> and you'll feel the difference immediately. It's free to start.</p>
<p>What's <em>your</em> experience been with dictation? If you wrote it off years ago, I'd genuinely love for you to give <a href="https://tonguetype.app/">TongueType</a> a shot and tell me what you think.</p>
]]></description>
      <pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://www.abeautifulsite.net/posts/i-mostly-stopped-typing/</link>
      <dc:creator>A Beautiful Site</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5282549909</guid>
    </item>
    <item>
      <title><![CDATA[Progressively enhancing Grid Lanes]]></title>
      <description><![CDATA[
                                <p>This post is a follow-up on my previous post. I&nbsp;was wondering whether it's safe to use Grid Lanes today. I came up with a solution I find okay, but there is a caveat.</p><hni-info>
<p>
  <strong>Note:</strong>
  This post contains interactive demos. If you want to see them instead of screenshots, enable the <code>CSS Grid Lanes Layout</code> flag in Edge or Chrome or use Safari 26.4+ or Polypane 29+.
</p>
</hni-info>
<p>In my last blog post, <a href="https://www.matuzo.at/blog/2026/grid-lanes-accessibility">Your Grid Lanes will likely fail WCAG 2.4.3</a>, I explained how Grid Lanes are inherently inaccessible by default unless you take extra measures. I also concluded that it's probably only safe to use Grid Lanes with Reading Flow. <a href="https://youtu.be/X6azWrtHS-k?t=1006">According to Rachel Andrew</a>, Google Chrome also made sure to first ship Reading Flow and then Grid Lanes. Unfortunately, that's not what all browsers did. Safari shipped it, but doesn't support Reading Flow yet.</p>
<h2 id="is-it-a-big-deal">Is it a big deal?</h2>
<p>Now you may think it's not a big deal because most people won't use Grid Lanes anyway until all browsers support it, and maybe, when that happens, Safari will also support Reading Flow. The thing is, Grid Lanes share many properties with Grids, and because CSS is beautifully designed, it's very easy to convert regular grids into Grid Lanes for browsers that support it.</p>
<p class="code-label">HTML</p>
<pre><code class="language-html">&lt;div class="grid-lanes"&gt;
  &lt;button class="s"&gt;1&lt;/button&gt;
  &lt;button class="m"&gt;2&lt;/button&gt;
  &lt;button class="l"&gt;3&lt;/button&gt;
  &lt;button class="l"&gt;4&lt;/button&gt;
  &lt;button class="m"&gt;5&lt;/button&gt;
  &lt;button class="s"&gt;6&lt;/button&gt;
&lt;/div&gt;</code></pre>
<p>In one line, you set the display property to <code>grid</code>, and in the next, you overwrite it with <code>grid-lanes</code>.</p>
<p class="code-label">CSS</p>
<pre><code class="language-css">.grid-lanes {
  display: grid;
  display: grid-lanes;
  grid-template-columns: repeat(3, 1fr);
}</code></pre>
<style>
.grid-lanes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--oli-g-spacing-s);
  background: var(--oli-g-colors-background);
  @supports (display: grid-lanes) and (reading-flow: grid-rows) {
    display: grid-lanes;
    reading-flow: grid-rows;
  }
}
.grid-lanes-flow { flow-tolerance: infinite;}
.supports-grid-lanes { display: none; }
@supports (display: grid-lanes) {
  .supports-grid-lanes { display: block;}
  .supports-not-grid-lanes { display: none; }
}
@supports not (display: grid-lanes) {
.supports-not-grid-lanes { display: block; }
}
figure p { margin: 0 !important; }
.focus-btn:focus {outline: 3px solid var(--oli-g-colors-cta)}
</style>
<p>Browsers that support it will create Grid Lanes, and those that don't will just fall back to a regular Grid.</p>
<figure>
<img alt="6 items with the same height placed in three columns form top to bottom, left to right." height="238" loading="lazy" src="https://www.matuzo.at/media/pages/blog/2026/grid-lanes-progressive-enhancement/48bc0ba6c4-1782798228/grid-no-support.avif" width="1000">
<figcaption>The grid in browsers that don't support CSS Grid Lanes</figcaption>
</figure>
<figure>
<img alt="6 differently-sized items placed in three columns. order from left to right: item 1, 2, 3, 6, 4, 5" height="207" loading="lazy" src="https://www.matuzo.at/media/pages/blog/2026/grid-lanes-progressive-enhancement/ff71826f52-1782798228/grid-support.avif" width="1000">
<figcaption>The grid in browser that support CSS Grid Lanes</figcaption>
</figure>
<p>In theory, that's all you need to create future-proof Grid Lanes. People will use it, and some are actually already doing it. <a href="https://adactio.com/journal/22613">Jeremy Keith</a>, for example, explains how he uses <code>@supports</code> to enhance a gallery built with Flexbox.</p>
<p>Generally, both strategies are fine, but neither addresses the focus order issue you may run into.</p>
<h2 id="an-accessible-progressive-enhancement-strategy">An accessible progressive enhancement strategy</h2>
<p>Although progressive enhancement is possible by merely repeating the <code>display</code> property, I suggest using Grid Lanes only if the browser supports Grid Lanes <strong>and</strong> Reading Flow.</p>
<pre><code class="language-css">.grid-lanes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);

  @supports (display: grid-lanes) and (reading-flow: grid-rows) {
    display: grid-lanes;
    reading-flow: grid-rows;
  }
}</code></pre>
<figure>
<div class="grid-lanes">
  <button class="l">1<br>&nbsp;<br>&nbsp;</button>
  <button class="m">2<br>&nbsp;</button>
  <button class="l">3<br>&nbsp;<br>&nbsp;</button>
  <button class="m">4<br>&nbsp;</button>
  <button class="m">5<br>&nbsp;</button>
  <button class="s">6</button>
</div>
<figcaption class="supports-not-grid-lanes">Demo: Items stacked from left to right, from top to bottom in a regular grid</figcaption>
<figcaption class="supports-grid-lanes">Demo: Items stacked from left to right, from top to bottom in Grid Lanes</figcaption>
</figure>
<p>If you test this demo in Chromium-based browsers with the feature flag enabled, you will see Grid Lanes, and in Firefox and in Safari, you'll see a regular grid. Yes, that means Safari users won't see Grid Lanes even though their browser supports it, but it also means that when browsers ship Reading Flow, they'll likely have a better, more accessible experience.</p>
<p>That's a great strategy, but…</p>
<h2 id="the-deal-breaker">The deal breaker</h2>
<p>I really like this idea, but unfortunately, Reading Flow is currently scoped to Grid and Flex containers. I wasn't able to find out whether there are plans to extend the property soon. The only thing you can do in the meantime is test your Grid Lanes with the keyboard and adjust <code>flow-tolerance</code> accordingly.</p>
<p>If you need a more general solution, set <code>flow-tolerance: infinite</code>, but still test with the keyboard.</p>
<pre><code class="language-css">.grid-lanes {
  display: grid;
  display: grid-lanes;
  grid-template-columns: repeat(3, 1fr);
  flow-tolerance: infinite;
}</code></pre>
<figure>
<img alt="6 differently-sized items placed in three columns. order from left to right: item 1, 2, 3, 4, 5, 6" height="207" loading="lazy" src="https://www.matuzo.at/media/pages/blog/2026/grid-lanes-progressive-enhancement/d722737809-1782798228/grid-tolerance.avif" width="1000">
<figcaption>The same demo from earlier but now the visual order matches the DOM order</figcaption>
</figure>
<h2 id="conclusion">Conclusion</h2>
<p>I suggest using the strategy that checks for support for both properties. It hinders you from shipping inaccessible layouts in Safari. Once <code>reading-flow</code> is extended, you can create well-balanced Grid Lanes while still maintaining a more or less expected focus order. Reading flow doesn't solve every problem, because focus can still follow a pretty wild path if the items used differ significantly in size, as described in my previous post.</p>
<p>The more I think about it, the less sympathy I have for Grid Lanes. You create a DOM order that you think makes sense, then you mess it up with CSS. Then, if you're aware of the problem, you fix it again with CSS. Finally, you have a patched-up layout that may have created other issues you can't address. </p>
<p>I'm looking forward to Chrome's, Firefox's, and Safari's next moves. Until then, to sum it up, my slightly adjusted recommendations are: </p>
<ul>
<li>Always test your Grid Lanes with the keyboard or a screen reader across different screen sizes.</li>
<li>If you find a mismatch between the DOM and the visual order, increase the <code>flow-tolerance</code> value. You may have to use a high value or even <code>infinite</code>.</li>
<li>As soon as it's available and supports Grid Lanes, try using <code>reading-flow</code> instead.</li>
<li>If that doesn't work, consider using a regular grid.</li>
</ul><p>My blog doesn't support comments yet, but you can reply via <a href="mailto:blog@matuzo.at?subject=Comment+on+%E2%80%9CProgressively+enhancing+Grid+Lanes%E2%80%9D">blog@matuzo.at</a>.</p>        ]]></description>
      <pubDate>Tue, 30 Jun 2026 06:01:45 +0000</pubDate>
      <link>https://www.matuzo.at/blog/2026/grid-lanes-progressive-enhancement</link>
      <dc:creator>Manuel Matuzović - Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5287396769</guid>
    </item>
    <item>
      <title><![CDATA[The Goalkeeper’s Parent]]></title>
      <description><![CDATA[
<p class="wp-block-paragraph">The most difficult position in soccer must be the goalkeeper’s parent.</p>
]]></description>
      <pubDate>Sat, 27 Jun 2026 14:13:28 +0000</pubDate>
      <link>https://actsofvolition.com/2026/06/the-goalkeepers-parent/</link>
      <dc:creator>Acts of Volition</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5284920414</guid>
    </item>
    <item>
      <title><![CDATA[Notion Mail is shutting down September 22]]></title>
      <description><![CDATA[<p><a href="https://x.com/NotionMail/status/2070177264457715817?ref=birchtree.me" rel="noopener noreferrer">Notion on X:</a></p><blockquote>We’re winding down the Notion Mail inbox across web, desktop, and iOS on September 22.</blockquote><p>It's a bit of a bummer, but truth be told, this didn't quite hit the mark. It was kind of like "Superhuman, but for cheap" when it launched, but once they started charging for the AI features, it just became "as expensive as Superhuman, but not quite as good". Once the price advantage went away, I reverted to Mimestream, myself.</p><p>If they wind down Notion Calendar, someone better hold me back, though.</p>]]></description>
      <pubDate>Fri, 26 Jun 2026 21:45:21 +0000</pubDate>
      <link>https://birchtree.me/blog/notion-mail-is-shutting-down-september-22/</link>
      <dc:creator>Birchtree</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5284243841</guid>
    </item>
    <item>
      <title><![CDATA[Still Holds: Gall’s Law]]></title>
      <description><![CDATA[<p>Complex systems evolve from simpler systems. The ones that thrive do so because they’ve adapted to real-world conditions — and not because they were designed to address all possibilities.</p>

<p>In systems thinking, this principle was best articulated by John Gall:</p>

<blockquote>
  <p>A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.</p>
</blockquote>

<p>I’ve long promoted <a href="https://jarango.com/2017/10/07/galls-law/">Gall’s law</a> to students and clients. It’s been hard going. We want to see products and services in their full glory ASAP. But you can only throw so much cash and person-hours at a problem. Ergo, we got the time-tested idea of a <em>minimum viable product</em>. (It’s no coincidence that orgs with more resources violate Gall’s law more often than scrappy startups.)</p>

<p>But the value of an MVP isn’t just that it allows you to get something that works quickly and cheaply. Instead, the value is that that first try isn’t <em>overspecified to theoretical conditions</em>. It’s only a draft meant to kick off an evolutionary process that leads to a system that meets real-world customer needs.</p>

<p>AI removes these constraints. An afternoon with Claude can yield a comprehensive spec for a very complex system. Further sessions can architect the system and build an initial release that includes bells, whistles, timpani, harps, violas, and all the other instruments in the orchestra. All this, at a fraction of the cost and time it would’ve taken in the past.</p>

<p>That’s amazing. It means we can now design and build much larger systems, faster. This opens new possibilities. Not just one new feature, a new product. Not just a new product, a suite. Not just a suite, a platform. The possibilities seem endless.</p>

<p>But in removing architecture and development constraints, we’re also removing the need to focus on what matters most and the discipline to run it by the market. All that upfront complexity doesn’t necessarily address real-world needs. Instead, it reflects a singular top-down vision that may or may not provide value to others.</p>

<p>Whereas MVPs in the “before times” called for minimal investment before validation, LLMs promise a much more realized vision from the first go. But there’s a significant difference between a sexy concept car and a vehicle customers will take grocery shopping. AI-augmented workflows will give you the former, but not the latter.</p>

<p>Here’s an example. Two years ago, I started building a product called SiteRanger, an AI-powered agent to help small teams manage large websites. This was before Claude Code or any of the current coding agents. Still, I got surprisingly far by using Claude to augment my basic PHP skills.</p>

<p>Together, we built a functional MVP that implemented what I considered to be the core functionality. The problem: my “core” was in fact an open-ended platform. Rather than solve a particular customer problem, it was designed to solve <em>classes</em> of problems.</p>

<p>By the time I got alpha users on board, the system was wildly over-architected. Worse, I learned new agentic systems could provide ~80% of its value. When I looked to pivot, I realized I’d have to move in a completely different direction, one with entrenched incumbents. It wasn’t worth it.</p>

<p>It wasn’t all a loss. This experiment taught me a lot about developing AI-powered software products using AI. But the most important lesson I learned is that AI makes it very easy for individuals and small teams to land in the same trap as resource-rich orgs: no constraints.</p>

<p>Which isn’t to say you shouldn’t use AI. To the contrary, I’m all for accelerating MVP design and development. But the word “viable” is fungible, especially when you have robot engineers. You want to expose the product to the discipline of the market. That means releasing something embarrassingly simple at first. And that requires discipline and constraint — the two things most scarce when working with LLMs.</p>

<p>My friend Karl Fast pointed me to a wonderful scene in the movie A RIVER RUNS THROUGH IT. The main character, a child, brings an essay to his dad, a strict preacher, for evaluation. The dad’s only reply: “Half as long.” The child does, and returns with the edit. After scribbling with a red pencil, the preacher looks at him and says: “Again, half as long.”</p>

<div class="embed-container youtube-wrapper">
  <iframe src="https://www.youtube.com/embed/36-VQQawpsk" allowfullscreen=""></iframe>
</div>

<p>Step away from the console and ask yourself:  What would I cut if there were no AI building it? Cut, cut, cut. Then imagine Tom Skerritt staring at you over his schoolmaster glasses and saying drily, “Again, half as long.”</p>]]></description>
      <pubDate>Fri, 26 Jun 2026 07:00:00 +0000</pubDate>
      <link>https://jarango.com/2026/06/26/still-holds-galls-law/</link>
      <dc:creator>Jorge Arango</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5283819639</guid>
    </item>
    <item>
      <title><![CDATA[Grid Lanes -- The Future of Masonry Layout]]></title>
      <description><![CDATA[<img src="https://www.oddbird.net/assets/images/winging-it/winging-it-33-1600w.jpeg" alt=""><p>This long-debated solution
for masonry layout
is now available
and it’s time
to figure out
how to use it.
Join us for a deep dive
into the workings of Grid Lanes.</p>
<div data-callout="note"><div>
<p>Check out our <a href="https://www.youtube.com/playlist?list=PL4jAKUtAhpHkXeLvZNoGHokWd0bo__NYy">Learn CSS Grid Playlist</a>
to learn more CSS.</p>
</div></div>
<figure class="embed">
      <div class="gallery"></div></figure>
<p class="main-action"><a href="http://www.youtube.com/channel/UCUkHxN78y9On9YH1zd-aTGw?sub_confirmation=1">Subscribe to Channel »</a></p>
<div class="anchor-link-wrapper">
<h2 id="what-we-cover%3A" tabindex="-1">What We Cover:</h2>
</div>
<ul>
<li><code>grid-lanes</code> is a new CSS display property that allows you to create a
masonry-like layout.</li>
<li><code>flow-tolerance</code> is a new property for <code>grid-lanes</code> that accepts a length
value.</li>
<li>Defining the <code>grid-lanes</code> with <code>grid-template-columns</code> creates a vertical
“waterfall” layout. <code>grid-template-rows</code> creates a “brick” layout.</li>
<li>Other grid features like gap, subgrid, and spanning a cell across multiple
tracks work with <code>grid-lanes</code>.</li>
<li>What alignment options do we have?</li>
<li>Can we fill in gaps that are created after spanning cells?</li>
</ul>
<div class="anchor-link-wrapper">
<h2 id="links%3A" tabindex="-1">Links:</h2>
</div>
<ul>
<li><a href="https://gridlanes.webkit.org/">The Field Guide to Grid Lanes</a></li>
<li><a href="https://courses.oddbird.net/poetic-css">Poetic CSS Course</a></li>
<li><a href="https://codepen.io/stacy/pen/jEyyKgb">CodePen demo</a></li>
<li><a href="https://patrickbrosset.com/articles/2026-06-19-alignment-in-css-grid-lanes/">Alignment is CSS Grid Lanes</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/display#browser_compatibility">Browser Compatibility</a></li>
</ul>
]]></description>
      <pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://www.oddbird.net/2026/06/18/winging-it-33/</link>
      <dc:creator>OddBird</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5307765544</guid>
    </item>
    <item>
      <title><![CDATA[Signs you're a dangerous terrorist: using Signal, moving zines]]></title>
      <description><![CDATA[<p>Link: <a href="https://www.theguardian.com/us-news/2026/jun/23/prairieland-ice-protesters-texas-sentenced?ref=werd.io"><em>Texas anti-ICE protesters convicted of terrorism charges sentenced to at least 50 years in prison, by Sam Levine in The Guardian</em></a></p><p>This is an outrageous litmus test for the freedom to protest in America:</p><blockquote>“A group of Texas protesters convicted of terrorism charges received unusually harsh sentences of at least 50 years in prison on Tuesday in a closely watched case that was widely seen as a test case of the Trump administration’s efforts to crack down on dissent.”</blockquote><p>Let’s be clear: a few of the protesters were out of bounds. One fired an AR-15 at the police, which goes beyond legitimate protest into inciting violence (and maybe even deliberate provocation). I would never condone that kind of activity.</p><p>But these sentences far outstrip anything that’s been given to anyone on the right wing: the leader of the Proud Boys, as this article notes, was sentenced to 22 years in prison. One protester wasn’t even present, but was sentenced to 30 years for moving some zines:</p><blockquote>“The ninth defendant, Daniel Sanchez-Estrada was not at the protest, but was convicted of corruptly concealing a document or record after prosecutors said he moved leftwing zines and other materials at the request of Rueda, his wife, after she was arrested. Sanchez-Estrada was sentenced to 30 years in prison on Tuesday.”</blockquote><p>Many of the protesters had guns and were part of a gun club. They all possessed them legally. I personally wish there was not a right to bear arms and think that their ubiquitous presence in America makes everyone less safe, but the right to own them is enshrined in the Second Amendment. Instead, other “evidence” was used to infer that they planned violence, including this specific argument that should give everyone pause:</p><blockquote>“[…] including their decision to communicate and auto-delete messages on Signal, an encrypted messaging platform widely used among activists, journalists and other citizens wary of government surveillance.”</blockquote><p>Collectively, the justice department argued that these convictions are proof that anti-fascists are terrorists, which should <em>also</em> give us pause. The precedent here is obviously very dangerous for freedom of speech, freedom of assembly, and democracy in America.</p>]]></description>
      <pubDate>Tue, 23 Jun 2026 18:16:37 +0000</pubDate>
      <link>https://werd.io/signs-youre-a-dangerous-terrorist-using-signal-moving-zines/</link>
      <dc:creator>Ben Werdmuller</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5279866794</guid>
    </item>
    <item>
      <title><![CDATA[A week in Ireland]]></title>
      <description><![CDATA[

<p>It started in Dublin. Myself and <a href="https://wordridden.com/">Jessica</a> got there in the afternoon and I went straight to <a href="https://thesession.org/sessions/432">The Cobblestone</a> in Smithfield for some tunes.</p>

<p>Then I went up the street to the headquarters of <a href="https://pipers.ie/">Na Píobairí Uilleann</a>, a beautiful Georgian building on Henrietta Street.</p>

<p>I was there to deliver my talk on the making of <a href="https://thesession.org">The Session</a>. There weren’t that many people there in person but quite a few people watched it live online. You can <a href="https://vimeo.com/1202605127">watch the video of the talk</a> if you want. I’m pretty pleased with it. The few times I played some tunes on my mandolin, the acoustics were lovely!</p>

<p>The next day we took the train down to Cork and onwards to my home town of Cobh. The town was looking its best. The weather was nice and the <a href="https://www.cunard.com/en-gb/cruise-ships/queen-anne/0">Queen Anne</a> was docked at the deep water quay.</p>

<p>We spent a lovely weekend hanging out with my mother, including a trip to Cork to see Michael Keegan Dolan’s latest dance piece, <a href="https://teacdamsa.com/production/1975/"><cite lang="ga">Naoi Déag Seachtó Cúig</cite></a>. It was joyous! Normally I’d get irrated by someone in the seat behind me tapping their foot, but <em>everyone</em> was tapping along to <a href="https://thesession.org/recordings/293">the classic first album by The Bothy Band</a>.</p>

<p>On Monday morning Jessica flew back to Brighton, leaving me to spend the week at the inaugural <a href="https://www.irishmandogathering.com/">Irish Mandolin Gathering</a>.</p>

<p>When I saw that this event was going to be happening, I thought “I’m going! Wherever it’s going to be, I’ll make my way there.” Then I saw it was happening in Little Island and I couldn’t believe my luck! Little Island is halfway between Cobh and Cork, easily reachable on the local trainline.</p>

<p>So I spent the week having a very pleasant commute. This was when the temperatures were getting dangerously high in England, but remained within reason in Ireland. Whenever anyone at the Mandolin gathering complained about the heat, I couldn’t help pointing out that we were actually in the coolest place in Europe for that week.</p>

<p>The mandolin nerdery was excellent. Lots of deep dives into technique, lots of trying out other people’s instruments, and of course, lots of playing tunes. <a href="https://seankeegan.ie/">Seán</a>, <a href="https://macdaraofaolain.bandcamp.com/">Macdara</a>, and <a href="http://marlafibish.com/">Marla</a> did a fantastic job, especially considering that this was the very first one!</p>

<p>The inaugural Irish Mandolin Gathering culminated with a concert at The White Horse in Ballincollig, which was excellent but every time it was mentioned, I had <a href="https://www.youtube.com/watch?v=Xi7KUmmjhsE">that John Spillane song</a> in my head.</p>

<p>Now I’m back home and feeling recharged from a thoroughly enjoyable week in Ireland. Next time I’ll be there will be for a week of learning Irish at <a href="https://www.oideasgael.ie/en">Oideas Gael in Donegal</a>—<cite lang="ga">táim ar bís!</cite></p>


            ]]></description>
      <pubDate>Tue, 30 Jun 2026 16:23:50 +0000</pubDate>
      <link>https://adactio.com/journal/22632</link>
      <dc:creator>Adactio: Journal</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5287972588</guid>
    </item>
    <item>
      <title><![CDATA[I’m joining The Type Founders!]]></title>
      <description><![CDATA[<p>While part of an agency, you’re always hoping to land that <em>dream</em> client: An organization aligned with your own interests and values, with stakeholders who support honest and open collaboration, engaged in challenging and inspiring work.</p>
<p>When the <a href="https://thetypefounders.com/"><strong>The Type Founders</strong></a> worked with <a href="https://cloudfour.com/">Cloud Four</a> last year, it quickly became clear they were that sort of client.</p>
<p>I was already an enormous fan of <a href="https://thetypefounders.com/foundries/">their world-class stable of foundries</a>. I’ve found their <a href="https://typographer.com/">Typographer</a> service indispensable since it was still invite-only. And it was a pleasure to collaborate with such an <a href="https://thetypefounders.com/about/">absurdly qualified team</a> of true believers in the craft of type design, solving problems I’d seen crop up again and again throughout my career.</p>
<p>(It certainly didn’t hurt that type, and specifically usability of leveraging digital type, has been a natural area of interest for me since long before the days of <a href="https://tylersticka.com/journal/cufon-and-sifr/">sIFR and cufón</a>!)</p>
<p>So when the time came to wrap up and hand off my UX recommendations, my heart felt unusually heavy. I was having a ball, and there was plenty to implement and iterate on. I wanted to keep going.</p>
<p>Thankfully, the enthusiasm was mutual, conversations continued… and eventually blossomed into a full-time opportunity!</p>
<p>I’ll be TTF’s <strong>Platform Experience Director</strong>, using my creative and technical skills in service of the typographic arts. I couldn’t be more excited!</p>
<p>I want to once again <a href="https://tylersticka.com/journal/cloud-four-minus-one/">thank Cloud Four</a> for 13+ years of making a better web together. And a huge thanks to family, friends and colleagues who lent an ear or offered words of support and encouragement throughout this process, it truly means the world to me.</p>]]></description>
      <pubDate>Tue, 30 Jun 2026 16:05:00 +0000</pubDate>
      <link>https://tylersticka.com/journal/the-type-founders/</link>
      <dc:creator>Tyler Sticka</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5287942076</guid>
    </item>
    <item>
      <title><![CDATA[So You Want to Fix Your All Hands]]></title>
      <description><![CDATA[
<p class="wp-block-paragraph">I will now explain why at least 50% of your team finds your current All Hands to be a waste of their time. They believe:</p>



<ol class="wp-block-list">
<li>This is information they can easily find elsewhere.</li>



<li>This is about you, not them.</li>



<li>This is boring.</li>
</ol>



<p class="wp-block-paragraph">Now, I will describe how to build an All Hands that will exceed their expectations. Let’s define the All Hands:</p>



<h2 class="wp-block-heading">Everyone. Together.</h2>



<p class="wp-block-paragraph">There’s an important organizational inflection point when you need the All Hands. The basic definition is, “The whole team — together.” If you’re a leader of a team of seven, then you might think your team meeting is your All Hands and — strictly speaking — that’s correct, but that’s your team or staff meeting. Not an All Hands.</p>



<p class="wp-block-paragraph">The whole team — together. This is a <strong>required</strong> meeting when the whole team is no longer able to be together organically. Is that 50? Maybe? Is that when they no longer fit in one room easily? Probably? The inflection point varies as a function of the company, but this is a required meeting because it mimics what you did instinctively when you were a smaller team, and that’s a good place to start.</p>



<p class="wp-block-paragraph">When it’s you, Frank, Aki, Liz, Gigi, and Joshua sitting in the same room cranking on your start-up, what are the events that everyone needs to regularly understand?</p>



<ol class="wp-block-list">
<li>What is the current state of the business?</li>



<li>When has something significant in the business changed?</li>



<li>What is important for us to do next?</li>
</ol>



<p class="wp-block-paragraph">In the same room, it is relatively easy to gather much of this information. You look around, see that Liz is on the phone closing her new head of engineering, and you think, “Go, Liz! I liked her candidate a lot.” Then you look over at Gigi, who has been quiet for two days and hasn’t smiled in four, and think, “She is the most focused designer in the world. I can’t wait to see what she’s done with our logo.” And so on.</p>



<p class="wp-block-paragraph">The ebb and flow of information in a group of humans decreases as a function of population size. With growth, your ability to organically discern what is going on within the team decreases<sup><a href="https://randsinrepose.com/archives/so-you-want-to-fix-your-all-hands/#footnote_1_5548" id="identifier_1_5548" class="footnote-link footnote-identifier-link" title="The folks who need the All Hands the most are the ones who are the least able to request it. See, your old guard employees, even in a rapidly growing team, are the best equipped to gather and share signal. This is why when someone new says, “We should do an All Hands,” they react negatively, “Why? That’s a big company meeting, and we’re a small group of high-hustle movers and shakers.” Yes, this meeting is not for the old guard; it’s for everyone who came after them.">1</a></sup>.</p>



<h2 class="wp-block-heading">Pick a Format. Keep It.</h2>



<p class="wp-block-paragraph">It’s not just the ebb and flow of information, but the chance that Critical Piece of Information X reaches Correct Person Y. Each additional human creates another opportunity for important information to not reach its intended recipient.</p>



<p class="wp-block-paragraph">As the senior leader, it’s trivial to forget this simple math because there’s a firehose pointed straight at your face. Your challenge is not the absence of information, it’s the abundance. Your challenge is picking out the signal from the noise while also not drowning.</p>



<p class="wp-block-paragraph">The firehose is the primary reason for my first piece of structure guidance: pick a format for your All Hands and stick with it. Here’s the format I’ve been using for a couple of decades:</p>



<ul class="wp-block-list">
<li>Hello, let’s learn something.</li>



<li>Here’s the current structure of the team and how it is different from the last time I showed this structure to you. If something huge changed or is about to change, there is room later to address this, or you can do it now.</li>



<li>Here’s an opportunity to hear from my direct reports.</li>



<li>Here’s a special guest.</li>



<li>No Q&amp;A. I’ll explain shortly.</li>
</ul>



<p class="wp-block-paragraph">I’ll explain the intent of each section in a moment, but the important thing to know right now is <strong>your structure will be different than mine, and that’s just fine.</strong> The point is not following my lead, but consistency. Anyone who receives an invite to this meeting understands broadly what to expect out of the meeting. Senior leaders who randomly do All Hands with fluid agendas at a time that suits them stress the team. When the unexpected and unexplainable meeting shows up, they instinctively think, “Uh oh. Someone’s in trouble.”</p>



<h2 class="wp-block-heading">Tree Talk</h2>



<p class="wp-block-paragraph">We open with Hello. Nothing fancy. Over the years, a simple hello seemed empty. I wanted to do a little more than say hello while folks were still gathering and getting into the All Hands mindset. At the last gig, I followed Hello with a single slide called “Tree Talk,” where I spent a few minutes teaching the team something interesting about trees. One slide, a few facts. Totally irrelevant to the team, but absolutely essential to the culture.</p>



<p class="wp-block-paragraph">Why? Because I believe curiosity is an intrinsic motivator. I don’t want the team sitting there as I drone on about things they already know; I want them to see what I care about. I care a lot about trees, but I care more about the team understanding the value of curiosity.</p>



<h2 class="wp-block-heading">One Slide. What Changed.</h2>



<p class="wp-block-paragraph">This is often the most boring part of the presentation, except when it isn’t. This is a picture of your organization. “The org chart.” Your job is to describe what has changed since the last time you threw this up on the screen with a little color commentary.</p>



<ul class="wp-block-list">
<li>It will feel tactical and obvious, but as I said above, this is because it’s your organization, and it’s likely you were involved in whatever changes occurred. Zero changes? Really? OK, you still show the chart and briefly say, “Hey, nothing’s changed here. Next slide.”</li>



<li>If massive changes have occurred since the last time you showed this, it’s worth cutting something from later in the agenda below in order for you and your team to explain what changed on the team and why.</li>



<li><strong>One slide</strong>. Never more. Yes, for a large organization, this means you’ll need to simplify or abstract out some organizations into simpler buckets. That’s fine — it just means you have subteams that need their own All Hands.</li>
</ul>



<h2 class="wp-block-heading">Not Your Show</h2>



<p class="wp-block-paragraph">The bulk of the presentation is not you, but some or all of your direct reports. What are they going to discuss? Up to them. The greatest hits:</p>



<ul class="wp-block-list">
<li>We built something!</li>



<li>We learned something!</li>



<li>Everything changed on the team, and we need to explain.</li>
</ul>



<p class="wp-block-paragraph">There are more.</p>



<p class="wp-block-paragraph">We haven’t discussed the length of your All Hands, and now is a good time. The maximum amount of time is 90 minutes. Frequency? Minimum is once a quarter.</p>



<ul class="wp-block-list">
<li><strong>90 minutes</strong> is a lot of time. In my experience, the average human’s attention span in a meeting like this is 30 minutes, which is why I have the above-suggested agenda. Think of it as three acts. Act 1 sets the table. Act 2 is the heart of the matter — what the team is actually doing. Act 3 brings in the rest of the company — more on that in a moment. Each act gets about 30 minutes. Act 1 runs short unless the building’s on fire, Act 2 always runs long, and Act 3 you can cap.</li>



<li><strong>Once a quarter</strong> is the sweet spot for this meeting. Four times a year. Every month? Overkill and a waste of time. Once a year? Not an All Hands, it’s an Annual Report.</li>
</ul>



<p class="wp-block-paragraph">I’m talking timing right now because Act 2’s size is a function of the number of direct reports. Three? They should all have a slot — seven minutes per direct, but they’ll end up using 10. Anything more than three and you’ll need to devise a rotation between directs.</p>



<p class="wp-block-paragraph">Back to their agenda. You should roughly know what they are going to talk about, but don’t micromanage it. It’s their schtick. If they screw it up, give them feedback.</p>



<p class="wp-block-paragraph">You’re building the humans who will replace you. All the time. This is one of the many ways you do that.</p>



<h2 class="wp-block-heading">The Mystery Guest</h2>



<p class="wp-block-paragraph">For Act 3, you want to bring in someone else for a fireside chat. Who? Depends on your team or your company. Start with known folks who are obvious co-conspirators. Folks you personally know with an existing rapport. Write 10 or so questions that you want to ask them, and send them the questions a week before. Warn them that you might go off-script — because you might — and see if they are OK with such vibes.</p>



<p class="wp-block-paragraph">When you get to the External Speaker portion of the All Hands, sit down next to them and start asking the questions. The most important advice? Listen to their answers. By far, the most interesting bits of this interview are the new things you discover via the conversation.</p>



<p class="wp-block-paragraph">After you’ve done a handful of familiar folks and have a well-refined set of questions, start asking humans you don’t know. Folks you’ve heard of that the team has worked with, but don’t know. My favorite one? I had the CEO show up once as a mystery guest. Fascinating 30 minutes.</p>



<p class="wp-block-paragraph">Which reminds me. Tell no one who the speaker will be, except the humans who must know.<sup><a href="https://randsinrepose.com/archives/so-you-want-to-fix-your-all-hands/#footnote_2_5548" id="identifier_2_5548" class="footnote-link footnote-identifier-link" title="While I am preaching consistency, with regard to your External Speaker, I like keeping it a mystery because it will, eventually, give your team more motivation to attend. I wonder who it is this time!">2</a></sup></p>



<h2 class="wp-block-heading">No Q&amp;A?</h2>



<p class="wp-block-paragraph">A controversial take, but an earned one. If I’m doing a talk at a conference, the Q&amp;A portion of the presentation is my favorite. The questions quickly tell me how well I delivered the core messages of my talk. Even the random ones that have very little to do with my talk.</p>



<p class="wp-block-paragraph">All Hands meetings are a different beast. Yes, it is a series of presentations and, yes, there are core messages delivered, but the large surface area covered in ninety minutes plus the size of the audience usually means questions:</p>



<ul class="wp-block-list">
<li>Feel random, and/or</li>



<li>Purely serve the agenda of the human asking the question.</li>
</ul>



<p class="wp-block-paragraph">However, you <strong>must</strong> create one more venue for the team to ask questions. Think Slack, your favorite message app, or via staff meetings. Even if you don’t, don’t worry, the important questions will find you.</p>



<h2 class="wp-block-heading">Entropy Crushing</h2>



<p class="wp-block-paragraph">Being social is work. Especially for engineers. We’d much prefer to be jamming at our desktop. Each time I’ve landed at a new gig where there was no culture of All Hands, I was disappointed by the attendance at first. 50% or worse.</p>



<p class="wp-block-paragraph">Yes, you can order donuts, and more will show up, but the real draw should be the presentation.</p>



<p class="wp-block-paragraph">They should leave with the lesson: <em>This is where I will learn.</em></p>



<p class="wp-block-paragraph">A regular All Hands meeting is inoculation. You are inoculating your team with quality information. This information will be used to disassemble gossip, rumors, and lies. This information will answer questions before they are asked. This information will remove noise so the human can focus on why they are there — to build.</p>
<ol class="footnotes"><li id="footnote_1_5548" class="footnote">The folks who need the All Hands the most are the ones who are the least able to request it. See, your old guard employees, even in a rapidly growing team, are the best equipped to gather and share signal. This is why when someone new says, “We should do an All Hands,” they react negatively, “Why? That’s a big company meeting, and we’re a small group of high-hustle movers and shakers.” Yes, this meeting is not for the old guard; it’s for everyone who came after them.</li><li id="footnote_2_5548" class="footnote">While I am preaching consistency, with regard to your External Speaker, I like keeping it a mystery because it will, eventually, give your team more motivation to attend. <em>I wonder who it is this time!</em></li></ol>]]></description>
      <pubDate>Mon, 29 Jun 2026 14:16:48 +0000</pubDate>
      <link>https://randsinrepose.com/archives/so-you-want-to-fix-your-all-hands/</link>
      <dc:creator>Rands in Repose</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5286662915</guid>
    </item>
    <item>
      <title><![CDATA[“Turn your site into a place people can bump into...]]></title>
      <description><![CDATA[
         	<p>“Turn your site into a place people can bump into each other.” <a href="https://cauenapier.com/blog/townsquare_release">Cool feature where people visiting this website can chat in a virtual Town Square</a>: “a small strip populated by stick figures…at the bottom of every page”. See you there?
</p>
 

        
        
        
    ]]></description>
      <pubDate>Fri, 26 Jun 2026 14:14:53 +0000</pubDate>
      <link>https://kottke.org/26/06/0049203-cool-feature-where-people</link>
      <dc:creator>kottke.org</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5283851877</guid>
    </item>
    <item>
      <title><![CDATA[My Om Malik Story]]></title>
      <description><![CDATA[<p>If you have’t heard, <a href="https://om.co/2026/06/24/1966-2026/">Om Malik passed away</a>.</p>
<p>People are sharing <a href="https://news.ycombinator.com/user?id=cobbzilla">stories</a> of their <a href="https://news.ycombinator.com/item?id=48679995">graceful encounters</a> with him.</p>
<p>This one is mine.</p>
<hr>
<p>Back at the beginning of 2021, <a href="https://blog.jim-nielsen.com/2021/writing-in-2020-and-2021/">I set a goal to write 72 blog posts</a>.</p>
<p>I was puttering along, publishing whatever came to mind, mostly figuring that nobody was reading any of it.</p>
<p>But that was ok. The process was therapeutic and it helped clarify my professional thinking, so I kept going.</p>
<p>One day on Twitter I got a DM from someone with the handle <a href="https://x.com/om"><code>@om</code></a>.</p>
<p>“I don’t know who this is,” I thought, “but damn that is a <em>great</em> handle!”</p>
<p>Then I peaked at the follower count: over 1 million!</p>
<p>“WTF? Who is this???” I thought.</p>
<p>I’d never — then or since — been contacted by someone with such a high profile online.</p>
<p>How was I even on this person’s radar?</p>
<p>I continued on to his message:</p>
<blockquote>
<p>Jim I wanted to thank you for your blog. I am neither a developer or a designer but appreciate the web, the open web and in general normal, common sense writing from experts. </p>
<p>I have quietly enjoyed your work — and hope you hit the target of 72 posts in 2021. My highly selfish ask, as I know it will feed my brain good important stuff. </p>
<p>Have a wonderful weekend and a great writing year</p>
</blockquote>
<p>I was flabbergasted. Who was this person with such a high follower count saying such kind words and I’d never heard of him?</p>
<p>I quickly went to Google. He had <a href="https://en.wikipedia.org/wiki/Om_Malik">his own Wikipedia</a>.</p>
<p>“Om Malik…tech writer…founded Gigaom!” Ah-ha! I knew <a href="https://web.archive.org/web/20080607112840/http://gigaom.com/">Gigaom the company/blog</a>. It shaped a lot of my early exposure to the tech beat. I devoured it. I can still picture <a href="https://cdn.jim-nielsen.com/blog/2026/gigaom-logo.png">the logo</a> in my head!</p>
<p>Now I knew the man behind it. Knowledge unlocked!</p>
<p>I thanked him graciously for taking the time to send a message whose importance seemed incredibly lopsided in my favor.</p>
<p>I quote his message here because I still think about it on occasion. His words then (as well as <a href="https://om.co/2022/02/01/the-daily-om-02-01-2022/">later</a> <a href="https://om.co/2023/02/13/streams-are-not-dead0yet/">ones</a>) continue to lift me up on days when I feel like an imposter. They remind me of the power of a small act of kindness, even within such a vast world wide web.</p>
<p>I still think about his words.</p>
<p>I still think about <em>him</em>.</p>
<p>I’m sure many will for some time.</p>
<p>And <em>that</em> is a legacy.</p>

    <hr>
    

    <p>
      Reply via:
      

      <a href="mailto:jimniels%2Bblog@gmail.com?subject=Re:%20blog.jim-nielsen.com/2026/tribute-to-om/">Email</a>
      · <a href="https://mastodon.social/@jimniels">Mastodon</a> ·

      <a href="https://bsky.app/profile/jim-nielsen.com">Bluesky</a>
    </p>

    
  ]]></description>
      <pubDate>Thu, 25 Jun 2026 19:00:00 +0000</pubDate>
      <link>https://blog.jim-nielsen.com/2026/tribute-to-om/</link>
      <dc:creator>Jim Nielsen’s Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5283289450</guid>
    </item>
    <item>
      <title><![CDATA[5,000 Restaurant Menus, Years 1880-1920]]></title>
      <description><![CDATA[Explore the Buttolph Collection from the New York Public Library's archive of historic menus.]]></description>
      <pubDate>Tue, 23 Jun 2026 04:00:00 +0000</pubDate>
      <link>https://pudding.cool/2026/06/menu-collection</link>
      <dc:creator>The Pudding</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5279725921</guid>
    </item>
    <item>
      <title><![CDATA[Every Choice Changes Everything: The Show]]></title>
      <description><![CDATA[<img src="https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/2026/06/signal-2026-06-03-144353_002.jpeg" alt="Every Choice Changes Everything: The Show"><p>About 3 weeks ago, <a href="https://en.wikipedia.org/wiki/Leo_Laporte" rel="noreferrer">Leo Laporte</a> and I recorded the first episode of what will be a new monthly show on the <a href="https://twit.tv/" rel="noreferrer">TWiT network</a>. Naming things is hard, and we almost voted on the name, like we did for <a href="https://blog.codinghorror.com/help-name-our-website/" rel="noreferrer">Stack Overflow</a>, but we quickly landed on <a href="https://twit.tv/obo" rel="noreferrer"><strong>Off By One with Jeff Atwood</strong></a> – which is funny for so many reasons, but mainly because of this programmer joke:</p><figure class="kg-card kg-image-card"><a href="https://www.martinfowler.com/bliki/TwoHardThings.html"><img src="https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/2026/05/image.png" class="kg-image" alt="Every Choice Changes Everything: The Show" loading="lazy" width="1083" height="326" srcset="https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/size/w600/2026/05/image.png 600w, https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/size/w1000/2026/05/image.png 1000w, https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/2026/05/image.png 1083w" sizes="(min-width: 720px) 720px"></a></figure><p>No, I did not come up with this variation on <a href="https://www.martinfowler.com/bliki/TwoHardThings.html" rel="noreferrer">the classic quote</a>, but I wish I had. Well, whatever, here's show number two – free to view for everyone.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/jveUllo7Mjs?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Club TWiT: Off By One With Jeff Atwood #2 - From Stack Overflow to Stay Gold, America"></iframe></figure><p>The show is 1h 47m of pure joy end to end. No negativity, just low-level insanity and of course, <em>mandatory fun</em>. We record the next episode in 4 days – and there's a live stream for <a href="https://twit.tv/clubtwit" rel="noreferrer">Club TWiT Members</a>. </p><p>(Let's do this. If, and <em>only if</em>, you watched the whole episode and liked what you saw ... for the first 10 people that <a href="https://forms.gle/y5STN9zQREQbt7Na6" rel="noreferrer">fill out this form</a>, I'll cover your Club TWiT membership for one full year so you can see if you enjoy the rest of the programming.)</p><p> The permanent show homepage is at <a href="https://twit.tv/obo" rel="noreferrer">twit.tv/obo</a>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><a href="https://twit.tv/obo"><img src="https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/2026/06/fca2de3652b07a0e.jpeg" class="kg-image" alt="Every Choice Changes Everything: The Show" loading="lazy" width="2000" height="2000" srcset="https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/size/w600/2026/06/fca2de3652b07a0e.jpeg 600w, https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/size/w1000/2026/06/fca2de3652b07a0e.jpeg 1000w, https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/size/w1600/2026/06/fca2de3652b07a0e.jpeg 1600w, https://storage.ghost.io/c/eb/aa/ebaa2665-01a8-4415-8825-69d1f0e8fd19/content/images/2026/06/fca2de3652b07a0e.jpeg 2000w" sizes="(min-width: 720px) 720px"></a><figcaption><span style="white-space: pre-wrap;">art by the incredibly talented </span><a href="https://www.claygrahamart.com/"><span style="white-space: pre-wrap;">claygrahamart.com</span></a></figcaption></figure><blockquote>Jeff Atwood, co-founder of <a href="https://stackoverflow.com" rel="noreferrer">Stack Overflow</a> and <a href="https://discourse.org" rel="noreferrer">Discourse</a>, creator of the <a href="https://blog.codinghorror.com/" rel="noreferrer">Coding Horror blog</a>, joins Leo Laporte monthly for a conversation that follows its own logic. Prop comedy, computing history, the open web, wealth inequality, yo-yos. Off by one topic at all times, in the best possible way.</blockquote><p>This is another way for Leo and I to share our enthusiasm for positive stuff in tech, and <a href="https://blog.codinghorror.com/on-the-meaning-of-coding-horror/" rel="noreferrer">sharing is crucial</a> because...</p><blockquote>I realized, that’s it. That’s it exactly. That is what is so intensely satisfying about writing here.&nbsp;<strong>My happiness only becomes real when I share it with all of you.</strong></blockquote><p>Now, thanks to <a href="https://wesleyfaulkner.com/" rel="noreferrer">Wesley Faulkner</a>, who introduced me to Leo and recommended I appear on a show, every month we can <strong>make our happiness real </strong>in a completely different dimension than writing alone – via the expressions on our faces, the tone of our voices, our body language. In other words, you can see and hear <strong>how we <em>feel</em></strong>.</p><p>Here are 3 key quotes from the second Off By One episode, with linked timestamps, so you can jump directly to that section.</p><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/jveUllo7Mjs?si=-zx-IWmxldVCPf4w&amp;t=2748" class="kg-btn kg-btn-accent">Off By One, Episode #2, 45:47</a></div><blockquote>Jeff: I am not an elite coder by any stretch of the imagination... I am very persistent.<br><br>Leo: Isn't that funny? Because people... I mean, I think of you as one of the voices, one of the chief voices, in coding<br><br>Jeff: Well, I advocate for code that doesn't kill you in so many different ways. You know, survivable code. Ideally, no code at all.<br><br>Leo: Right.<br><br>Jeff: That's the best code, is none.<br><br>Leo: Right.<br><br>Jeff: It's a bit of a zen statement but it's true. So, I'm an advocate for, you know, good engineering. Good process. A process that recognizes that we're human and we should do this together, and we should actually kinda like each other, even.</blockquote><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/jveUllo7Mjs?si=NSLiTu3me0Bt37ro&amp;t=3937" class="kg-btn kg-btn-accent">Off by One, Episode #2, 01:05:37</a></div><blockquote>Jeff: Dad's funny. He had kind of a dark sense of humor that I enjoyed. Betsy doesn't like it so much, and not too much, and I get it, but I enjoy it. And I called it "the last season of the John Atwood show". It's gonna be a real banger! And it <strong>was</strong>, it f***** was, it really was! Because we won capitalism, and then <strong>we went back and made it better for everyone</strong>. I don't think it gets better than that for me.</blockquote><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/jveUllo7Mjs?si=y507y3c2rzZXy8iA&amp;t=4082" class="kg-btn kg-btn-accent">Off by One - Episode #2, 01:08:02</a></div><blockquote>Jeff: And the other thing is, you can just run the math on this, I've posted several times on Mastodon and other places like LinkedIn, I've done some research and if we simply collected a fair tax from Billionaires, we could literally elimate all poverty in this country at the 100% FPL level, which is $15,000 per year. <strong>All poverty</strong>. We would have zero poverty. We have the <strong>means</strong> to do it.<br><br>Leo: That's really important.<br><br>Jeff: We lack the <strong>will</strong>.</blockquote><p>If you want to witness the chaotic good of my original guest appearance which led to this show, watch <strong>the first 45 minutes </strong>of <a href="https://twit.tv/shows/intelligent-machines/episodes/859" rel="noreferrer">Intelligent Machines #859</a>, recorded on Feb 25th along with <a href="https://parismartineau.com/" rel="noreferrer">Paris Martineau</a>, <a href="https://en.wikipedia.org/wiki/Jeff_Jarvis" rel="noreferrer">Jeff Jarvis</a>, and Leo. I dialed down the chaos considerably for the Off By One show, but for this one, I personally think it's funnier to watch Paris' reaction to me for the entire show. <em>You've been warned!</em></p><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/h_DWm6ReuaI?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="What's Behind the Fox? - Tech's Gilded Age"></iframe></figure><p>Here are 3 key quotes from this episode, with linked timestamps, so you can jump directly to that section.</p><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/h_DWm6ReuaI&amp;t=1828s" class="kg-btn kg-btn-accent">Intelligent Machines #859 - What's Behind the Fox? 30:28</a></div><blockquote>Leo: Well in a way it's a shame because we have in the last year kind of stepped back from our global initiatives in the united states and I think we do have a responsibility. I think your partner is absolutely right. <strong>If you have everything you need, then help others have everything they need</strong>.<br><br>Jeff: What is money even FOR? I don't even have "that much", what do you.. how do you spend it all? I don't have.. I just want a simple life, man!</blockquote><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/h_DWm6ReuaI?t=1979" class="kg-btn kg-btn-accent">Intelligent Machines #859 - What's Behind The Fox? 32:59</a></div><blockquote>Jeff: I mean.. have you seen some of the stuff LLMs will do when you tell them to optimize? It's like, optimize this for 95% and it's like okay, "return true".<br><br>Leo: That's a good optimization!<br><br>Jeff: Well, because it doesn't know what it's doing. It has no actual understanding. It's playing a game of global brain statistics and copy paste. And it's good at like, merging... I call it <strong>JPEG for words</strong>, which it kind of is. And there's so much stuff. It's like reading summaries. And it is very accurate with summaries. We saw this on discourse. They implemented it. I was very skeptical. And I went to some very complex discussions. We had on our internal discourse and read the summary and was like <strong>that is a very good summary</strong> and it captured the key points in the discussion. It could have captured more, but it got nothing wrong. And it basically <strong>was JPEG for that conversation, wasn't it.. </strong>without much loss.<br><br>Jeff: Now does JPEG work on EVERY image? No. Garfield is a bad choice, for, yknow, JPEG.</blockquote><div class="kg-card kg-button-card kg-align-left"><a href="https://youtu.be/h_DWm6ReuaI&amp;t=2530" class="kg-btn kg-btn-accent">Intelligent Machines #859 - What's Behind The Fox? 42:10</a></div><blockquote>Jeff Atwood – Ok, the first guilded age, we're deep in the second one now. I mean, just look up the numbers. More money in the hands of fewer people than in any other period of  time. In the first guilded age, that was basically the railroad barons. Guess who it is in the second guilded age? <em>I'm in this picture and I don't like it.  </em>So like, <strong>what are we gonna do about it?</strong></blockquote><p>So thank you, Leo and Wesley, for giving me another way to make happiness real by sharing it with all of you, now in video and audio form, all the feels, all the time. Well, once per month.</p><p>Let us know what you think –  I don't mind comments here but I'm much more likely to answer on the <a href="https://www.twit.community/" rel="noreferrer">TWiT community Discourse</a>. Try on a paragraph for size, our old pal the pilcrow ¶. You might even like it! It's possible the practice of writing paragraphs and forming coherent narratives might even improve your overall writing and communication skills. <a href="https://blog.codinghorror.com/nobodys-going-to-help-you-and-thats-awesome/" rel="noreferrer">Or your life, even. </a></p><figure class="kg-card kg-embed-card"><iframe width="200" height="150" src="https://www.youtube.com/embed/FHgAXOubLCU?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="Discourse sticker &quot;Power of the Paragraph&quot;"></iframe></figure><p></p><p>I also heard a rumor that any Club TWiT users who make their way from the Discord and post regularly on the <a href="https://www.twit.community/" rel="noreferrer">TWiT Discourse</a> might get a super cool little token of appreciation in the postal mail from some user named "Junk". Who knows? Who can say what might happen? 🤔</p>]]></description>
      <pubDate>Mon, 22 Jun 2026 22:10:42 +0000</pubDate>
      <link>https://blog.codinghorror.com/every-choice-changes-everything-the-show/</link>
      <dc:creator>Coding Horror</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5278870223</guid>
    </item>
    <item>
      <title><![CDATA[Setting the width of selects to the width of the selected option]]></title>
      <description><![CDATA[
                                <p>The <a href="https://drafts.csswg.org/css-forms-1/#propdef-field-sizing">field-sizing property</a> is coming to Firefox 152, making it available across all major engines. It allows you to control the sizing behavior of elements with a default preferred size, such as form elements.</p><p>I'll take a closer look at the field-sizing property in another blog post,  but I wanted to show you a cool feature first.</p>
<p>By default, the width of a select element is determined by its largest option.</p>
<p>
<label for="place">Pick a city</label>
<select id="place">
  <option>Amsterdam</option>
  <option>Rotterdam</option>
  <option>Scheveningen</option>
  <option selected="">Utrecht</option>
</select>
</p>
<details>
<summary>Show code</summary>
<pre><code class="language-html">&lt;label for="place"&gt;Pick a city&lt;/label&gt;
&lt;select id="place"&gt;
  &lt;option&gt;Amsterdam&lt;/option&gt;
  &lt;option&gt;Rotterdam&lt;/option&gt;
  &lt;option&gt;Scheveningen&lt;/option&gt;
  &lt;option selected&gt;Utrecht&lt;/option&gt;
&lt;/select&gt;</code></pre>
</details>
<p>You can see how the select element here is much wider than the width of the characters in the word “Utrecht”. It’s large enough to fit “Scheveningen”, the largest option. That may or may not be desired. At least, I remember occasions in the past where I wanted it to be only as large as the selected option. With field-sizing that's now possible.</p>
<pre><code class="language-css">select {
  field-sizing: content;
}</code></pre>
<p>
<label for="place1">Pick a city</label>
<select id="place1" style="field-sizing: content;">
  <option>Amsterdam</option>
  <option>Rotterdam</option>
  <option>Scheveningen</option>
  <option selected="">Utrecht</option>
</select>
</p>
<p>Now you can see how the width of the select changes with its selected option.</p><p>My blog doesn't support comments yet, but you can reply via <a href="mailto:blog@matuzo.at?subject=Comment+on+%E2%80%9CSetting+the+width+of+selects+to+the+width+of+the+selected+option%E2%80%9D">blog@matuzo.at</a>.</p>        ]]></description>
      <pubDate>Mon, 22 Jun 2026 05:41:21 +0000</pubDate>
      <link>https://www.matuzo.at/blog/2026/select-fieldsize</link>
      <dc:creator>Manuel Matuzović - Blog</dc:creator>
      <guid isPermaLink="false">https://feedbin.me/entries/5277996860</guid>
    </item>
  </channel>
</rss>
