<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Notes - Alex Carpenter</title><description>Notes on engineering, developer experience, design systems, and accessibility.</description><link>https://alexcarpenter.me/</link><item><title>Tree structure using CSS anchor positioning</title><link>https://alexcarpenter.me/notes/2026-04-04-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-04-04-1/</guid><pubDate>Sat, 04 Apr 2026 18:35:11 GMT</pubDate><content:encoded>&lt;p&gt;One of the coolest uses of CSS anchor positioning I have seen.&lt;/p&gt;
</content:encoded></item><item><title>Nikhil Anand&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-03-27-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-27-1/</guid><pubDate>Fri, 27 Mar 2026 18:49:41 GMT</pubDate></item><item><title>Nguyễn Ngọc Ánh&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-03-21-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-21-2/</guid><pubDate>Sat, 21 Mar 2026 12:31:11 GMT</pubDate></item><item><title>William Jansson&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-03-21-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-21-1/</guid><pubDate>Sat, 21 Mar 2026 12:29:03 GMT</pubDate></item><item><title>Using Container Queries in a CSS Grid to Detect Truncation</title><link>https://alexcarpenter.me/notes/2026-03-20-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-20-1/</guid><pubDate>Fri, 20 Mar 2026 12:37:14 GMT</pubDate></item><item><title>Managing Context Windows with pi /tree: Branches, Summaries, and Subagent-like Workflows</title><link>https://alexcarpenter.me/notes/2026-03-17-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-17-1/</guid><pubDate>Tue, 17 Mar 2026 11:19:43 GMT</pubDate></item><item><title>Daniel Griesser&apos;s PI Config</title><link>https://alexcarpenter.me/notes/2026-03-15-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-15-1/</guid><pubDate>Sun, 15 Mar 2026 23:53:02 GMT</pubDate></item><item><title>The Anatomy of an Agent Harness</title><link>https://alexcarpenter.me/notes/2026-03-11-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-11-1/</guid><pubDate>Wed, 11 Mar 2026 11:18:23 GMT</pubDate></item><item><title>Agentic Engineering Patterns</title><link>https://alexcarpenter.me/notes/2026-03-08-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-08-1/</guid><pubDate>Sun, 08 Mar 2026 13:19:33 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Patterns for getting the best results out of coding agents like Claude Code and OpenAI Codex.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>React Grab</title><link>https://alexcarpenter.me/notes/2026-03-06-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-06-2/</guid><pubDate>Sat, 07 Mar 2026 00:23:27 GMT</pubDate></item><item><title>Smooth Dot Indicators with Embla Carousel and CSS color-mix()</title><link>https://alexcarpenter.me/notes/2026-03-06-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-06-1/</guid><description>How I implemented smooth carousel pagination indicators that respond to scroll progress</description><pubDate>Fri, 06 Mar 2026 13:43:22 GMT</pubDate><content:encoded>&lt;p&gt;The interview gallery on &lt;a href=&quot;https://ibrewmyown.coffee/interviews/fatih-arslan&quot;&gt;ibrewmyown.coffee&lt;/a&gt; uses image carousels with pagination dots. Standard pagination dots using Embla Carousel toggle between active and inactive, but I wanted them to respond to scroll progress: as you drag, the current indicator fades out while the next one fades in.&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.embla-carousel.com/&quot;&gt;Embla Carousel&lt;/a&gt; exposes scroll progress, making this possible:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const onScroll = (): void =&amp;gt; {
  const snapList = emblaApi.scrollSnapList();
  const progress = emblaApi.scrollProgress();

  if (snapList.length &amp;lt; 2) return;

  let lower = snapList.length - 2;
  for (let i = 0; i &amp;lt; snapList.length - 1; i++) {
    if (progress &amp;gt;= snapList[i] &amp;amp;&amp;amp; progress &amp;lt;= snapList[i + 1]) {
      lower = i;
      break;
    }
  }

  const upper = lower + 1;
  const range = snapList[upper] - snapList[lower];
  const t = range === 0 ? 0 : (progress - snapList[lower]) / range;

  dotNodes.forEach((_, i) =&amp;gt; {
    if (i === lower) setDotProgress(i, 1 - t);
    else if (i === upper) setDotProgress(i, t);
    else setDotProgress(i, 0);
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As &lt;code&gt;t&lt;/code&gt; interpolates from 0 to 1, a CSS custom property &lt;code&gt;--dot-progress&lt;/code&gt; is set for each dot. This single value drives two animations: width expansion and color interpolation.&lt;/p&gt;
&lt;h2&gt;Width and Color&lt;/h2&gt;
&lt;p&gt;The dot width expands as you approach it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;width: calc(var(--spacing) * 2 + var(--spacing) * 3 * var(--dot-progress, 0));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the color blends between two states using &lt;code&gt;color-mix()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;background-color: color-mix(
  in srgb,
  var(--color-neutral-600) calc(var(--dot-progress, 0) * 100%),
  var(--color-neutral-300)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach preserves contrast, keeping the active dot darkest and the inactive lightest. The browser handles both interpolations without additional JS overhead.&lt;/p&gt;
&lt;p&gt;Hat tip to &lt;a href=&quot;https://x.com/PixelJanitor/status/2029700875006922763&quot;&gt;Derek Briggs&lt;/a&gt; for the color fade idea: “What if the current active indicator faded out the primary color during the drag and the next active faded in so that the most active item had the most contrast always?”&lt;/p&gt;
</content:encoded></item><item><title>History of Software Design</title><link>https://alexcarpenter.me/notes/2026-03-05-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-05-1/</guid><pubDate>Thu, 05 Mar 2026 19:20:10 GMT</pubDate></item><item><title>How to implement beautiful shadow borders</title><link>https://alexcarpenter.me/notes/2026-03-04-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-04-1/</guid><pubDate>Wed, 04 Mar 2026 16:25:34 GMT</pubDate></item><item><title>Naz Hamid&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-03-01-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-01-2/</guid><pubDate>Sun, 01 Mar 2026 16:25:34 GMT</pubDate></item><item><title>Anton Repponen&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-03-01-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-03-01-1/</guid><pubDate>Sun, 01 Mar 2026 14:30:41 GMT</pubDate></item><item><title>ESLint complexity rule</title><link>https://alexcarpenter.me/notes/2026-02-24-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-24-1/</guid><pubDate>Tue, 24 Feb 2026 20:50:52 GMT</pubDate></item><item><title>&lt;TextMorph /&gt;</title><link>https://alexcarpenter.me/notes/2026-02-16-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-16-2/</guid><pubDate>Mon, 16 Feb 2026 16:48:22 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Dependency-free animated text component.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Building Type-Safe Compound Components</title><link>https://alexcarpenter.me/notes/2026-02-16-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-16-1/</guid><pubDate>Mon, 16 Feb 2026 13:21:11 GMT</pubDate></item><item><title>@nano_kit/store</title><link>https://alexcarpenter.me/notes/2026-02-15-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-15-2/</guid><pubDate>Sun, 15 Feb 2026 13:41:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@nano_kit/store&lt;/code&gt; is a lightweight state management library inspired by Nano Stores and built around a push-pull based reactivity system.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>react-prehydrate</title><link>https://alexcarpenter.me/notes/2026-02-15-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-15-1/</guid><pubDate>Sun, 15 Feb 2026 12:59:10 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Eliminate flash-of-incorrect-state for user preferences in React Server Component apps.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>ASCII characters are not pixels: a deep dive into ASCII rendering</title><link>https://alexcarpenter.me/notes/2026-02-14-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-14-1/</guid><pubDate>Sun, 15 Feb 2026 00:56:51 GMT</pubDate></item><item><title>How I Use Claude Code</title><link>https://alexcarpenter.me/notes/2026-02-13-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-13-1/</guid><pubDate>Fri, 13 Feb 2026 13:12:56 GMT</pubDate></item><item><title>Digital hygiene</title><link>https://alexcarpenter.me/notes/2026-02-12-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-12-1/</guid><pubDate>Thu, 12 Feb 2026 12:43:49 GMT</pubDate></item><item><title>eslint-plugin-react-render-types</title><link>https://alexcarpenter.me/notes/2026-02-11-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-11-2/</guid><pubDate>Wed, 11 Feb 2026 12:47:53 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;ESLint plugin that brings Flow’s Render Types to TypeScript via JSDoc. Enforce component composition constraints like &lt;code&gt;@renders {MenuItem}&lt;/code&gt; at lint time.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Matt Rothenberg&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-02-11-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-11-1/</guid><pubDate>Wed, 11 Feb 2026 12:43:56 GMT</pubDate></item><item><title>RESILIENT-UI SKILLS</title><link>https://alexcarpenter.me/notes/2026-02-09-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-09-1/</guid><pubDate>Mon, 09 Feb 2026 15:49:31 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;npx skills add alexcarpenter/resilient-ui
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Building Bulletproof React Components</title><link>https://alexcarpenter.me/notes/2026-02-06-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-06-2/</guid><pubDate>Fri, 06 Feb 2026 12:50:48 GMT</pubDate></item><item><title>A React trick to improve exit animations</title><link>https://alexcarpenter.me/notes/2026-02-06-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-06-1/</guid><pubDate>Fri, 06 Feb 2026 12:50:26 GMT</pubDate></item><item><title>Alexander Vilinskyy&apos;s personal website</title><link>https://alexcarpenter.me/notes/2026-02-03-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-03-2/</guid><pubDate>Tue, 03 Feb 2026 13:43:34 GMT</pubDate></item><item><title>before-and-after</title><link>https://alexcarpenter.me/notes/2026-02-03-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-02-03-1/</guid><pubDate>Tue, 03 Feb 2026 12:06:47 GMT</pubDate></item><item><title>Composition is all you need.</title><link>https://alexcarpenter.me/notes/2026-01-29-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-29-1/</guid><pubDate>Thu, 29 Jan 2026 12:21:29 GMT</pubDate><content:encoded>&lt;p&gt;See also &lt;code&gt;vercel-composition-patterns&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx skills add vercel-labs/agent-skills
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>When life gives you lemons, write better error messages</title><link>https://alexcarpenter.me/notes/2026-01-28-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-28-1/</guid><pubDate>Wed, 28 Jan 2026 11:47:36 GMT</pubDate><content:encoded>&lt;ol&gt;
&lt;li&gt;Say what happened&lt;/li&gt;
&lt;li&gt;Say why it’s a problem&lt;/li&gt;
&lt;li&gt;Point out how to solve the problem&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2026-01-26-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-26-1/</guid><pubDate>Mon, 26 Jan 2026 19:34:05 GMT</pubDate><content:encoded>&lt;p&gt;TIL you can slow down animations in Chrome DevTools.&lt;/p&gt;
&lt;p&gt;Press &lt;kbd&gt;cmd+shift+p&lt;/kbd&gt; (or &lt;kbd&gt;ctrl+shift+p&lt;/kbd&gt; on Windows/Linux), then search for “animations”. An Animations panel will appear at the bottom of DevTools where you can adjust the playback speed to 100%, 25%, or 10%. All animations on the page will now run at the selected speed.&lt;/p&gt;
</content:encoded></item><item><title>Greppability is an underrated code metric</title><link>https://alexcarpenter.me/notes/2026-01-22-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-22-1/</guid><pubDate>Thu, 22 Jan 2026 21:17:27 GMT</pubDate></item><item><title>Annotating for agents</title><link>https://alexcarpenter.me/notes/2026-01-19-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-19-1/</guid><pubDate>Mon, 19 Jan 2026 17:28:08 GMT</pubDate></item><item><title>21 Lessons From 14 Years at Google</title><link>https://alexcarpenter.me/notes/2026-01-17-01/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-17-01/</guid><pubDate>Sat, 17 Jan 2026 13:17:52 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2026-01-06-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2026-01-06-1/</guid><pubDate>Tue, 06 Jan 2026 22:02:58 GMT</pubDate><content:encoded>&lt;p&gt;CSS only scroll fade example that I implemented on &lt;a href=&quot;https://github.com/alexcarpenter/ibrewmyown.coffee/commit/b5458aa1d151b14b085394c7fa9f2263320f1384&quot;&gt;ibrewmyown.coffee&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@supports (animation-timeline: scroll()) {
  @property --fade-left {
    syntax: &quot;&amp;lt;length&amp;gt;&quot;;
    inherits: false;
    initial-value: 0px;
  }
  @property --fade-right {
    syntax: &quot;&amp;lt;length&amp;gt;&quot;;
    inherits: false;
    initial-value: 0px;
  }

  .scroll-fade {
    --fade-distance: 40px;
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 var(--fade-left),
      #000 calc(100% - var(--fade-right)),
      transparent 100%
    );
    mask-size: 100% 100%;
    mask-repeat: no-repeat;
    animation:
      fade-in-left 1 linear both,
      fade-out-right 1 linear both;
    animation-timeline: scroll(x self), scroll(x self);
    animation-range:
      0% 12%,
      88% 100%;
  }

  @keyframes fade-in-left {
    from {
      --fade-left: 0px;
    }
    to {
      --fade-left: var(--fade-distance);
    }
  }

  @keyframes fade-out-right {
    from {
      --fade-right: var(--fade-distance);
    }
    to {
      --fade-right: 0px;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>How to target Safari with a CSS @supports media query</title><link>https://alexcarpenter.me/notes/2025-12-15-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-12-15-1/</guid><description>Easiest method for targeting Safari with CSS and Tailwind in 2025.</description><pubDate>Mon, 15 Dec 2025 13:03:15 GMT</pubDate></item><item><title>2025 Holiday Coffee Gift Guide</title><link>https://alexcarpenter.me/notes/2025-12-03-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-12-03-1/</guid><description>Discover the best coffee gifts for the holidays—espresso machines, drippers, brewers, and bean subscriptions that coffee lovers will actually use every day.</description><pubDate>Wed, 03 Dec 2025 12:49:22 GMT</pubDate></item><item><title>RESILIENT—UI interview #002 with Hayden Bleasel</title><link>https://alexcarpenter.me/notes/2025-11-18-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-11-18-1/</guid><description>Australian Design Engineer who loves working on open source software for the web.</description><pubDate>Tue, 18 Nov 2025 13:30:55 GMT</pubDate></item><item><title>The Web Animation Performance Tier List</title><link>https://alexcarpenter.me/notes/2025-11-05-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-11-05-2/</guid><pubDate>Wed, 05 Nov 2025 15:20:46 GMT</pubDate></item><item><title>RESILIENT—UI interview #001 with Dima Belyaev</title><link>https://alexcarpenter.me/notes/2025-11-05-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-11-05-1/</guid><description>Staff front-end engineer based in Amsterdam, working on AI assistants at Shopify.</description><pubDate>Wed, 05 Nov 2025 12:52:09 GMT</pubDate></item><item><title>Preserve modal aspect ratio across viewport sizes</title><link>https://alexcarpenter.me/notes/2025-10-23-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-10-23-1/</guid><pubDate>Thu, 23 Oct 2025 12:20:33 GMT</pubDate><content:encoded>&lt;p&gt;This is one of those that feels like it should be easier than it ends up being, but here is where I landed at to ensure a modal image preserves its 3/2 aspect ratio no mater the viewport width/height.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.modal {
  aspect-ratio: 3 / 2;
  height: min(calc(100vh - 2rem), calc((100vw - 2rem) * 2 / 3));
  margin: auto;
  position: relative;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See it in action on &lt;a href=&quot;https://www.ibrewmyown.coffee&quot;&gt;I Brew My Own Coffee&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-09-28-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-28-1/</guid><pubDate>Sun, 28 Sep 2025 13:44:13 GMT</pubDate><content:encoded>&lt;p&gt;It’s always felt weird using opacity for disabled buttons for situations where its not placed on a solid background.&lt;/p&gt;
&lt;p&gt;In this alternative approach I am using color-mix to create a similar disabled effect without the opacity.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
  class=&quot;bg-[color-mix(in_srgb,var(--color-blue-500)_50%,var(--color-background))] text-[color-mix(in_srgb,var(--color-white)_50%,var(--color-background))]&quot;
  disabled
&amp;gt;
  Hello world
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Dither plugin for TailwindCSS</title><link>https://alexcarpenter.me/notes/2025-09-20-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-20-1/</guid><pubDate>Sat, 20 Sep 2025 12:39:55 GMT</pubDate></item><item><title>▲ Vercel Web Interface Guidelines</title><link>https://alexcarpenter.me/notes/2025-09-19-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-19-1/</guid><pubDate>Fri, 19 Sep 2025 12:23:59 GMT</pubDate><content:encoded>&lt;p&gt;See also Rauno’s &lt;a href=&quot;https://interfaces.rauno.me/&quot;&gt;Web Interface Guidelines&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-09-12-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-12-1/</guid><pubDate>Fri, 12 Sep 2025 22:19:56 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been fortunate to work with some amazing people over the years and a couple of those folks are open for &lt;a href=&quot;/rolodex&quot;&gt;new opportunities&lt;/a&gt;. Scoop them up.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-09-06-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-06-1/</guid><pubDate>Sat, 06 Sep 2025 19:33:30 GMT</pubDate><content:encoded>&lt;p&gt;Fixing folks list alignment one PR at a time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/midday-ai/midday/pull/606&quot;&gt;midday/pull/606&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dodopayments/billingsdk/pull/144&quot;&gt;billingsdk/pull/144&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-09-05-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-05-2/</guid><pubDate>Fri, 05 Sep 2025 12:58:10 GMT</pubDate><content:encoded>&lt;p&gt;Two fantastic pieces of web inspiration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://operate.so&quot;&gt;operate.so&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tempo.xyz/&quot;&gt;tempo.xyz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>My shadcn/ui registry</title><link>https://alexcarpenter.me/notes/2025-09-05-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-09-05-1/</guid><pubDate>Fri, 05 Sep 2025 12:31:49 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been slowly working on my own shadcn/ui registry while learning more about the whole component distrubution setup at work. So far I have the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.alexcarpenter.me/#info-list&quot;&gt;&lt;code&gt;&amp;lt;InfoList /&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;br /&gt;
A list component with support for items, headers, and icons slots.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.alexcarpenter.me/#inline-text&quot;&gt;&lt;code&gt;&amp;lt;InlineText /&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;br /&gt;
An inline text component with an icon slot that wraps perfectly with the text.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ui.alexcarpenter.me/#pricing-table&quot;&gt;&lt;code&gt;&amp;lt;PricingTable /&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;br /&gt;
A pricing table component with support for monthly/yearly pricing.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>A Clock That Doesn&apos;t Snap</title><link>https://alexcarpenter.me/notes/2025-08-26-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-26-2/</guid><pubDate>Tue, 26 Aug 2025 15:34:53 GMT</pubDate></item><item><title>What are OKLCH colors?</title><link>https://alexcarpenter.me/notes/2025-08-26-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-26-1/</guid><pubDate>Tue, 26 Aug 2025 15:32:27 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2025-08-25-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-25-1/</guid><pubDate>Mon, 25 Aug 2025 12:39:17 GMT</pubDate><content:encoded>&lt;p&gt;Another use case for absolute positioning elements within inline text to support hanging punctuation.&lt;/p&gt;
&lt;p&gt;Supports varying sizes of text, without the need for custom negative text indent values.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p class=&quot;relative inline-block&quot;&amp;gt;
  &amp;lt;span class=&quot;absolute right-full&quot;&amp;gt;“&amp;lt;/span&amp;gt;Lorem ipsum dolor sit amet
  consectetur adipisicing elit. Iste officia quasi fugiat, dolores ab nam
  repellendus voluptate”
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Silk</title><link>https://alexcarpenter.me/notes/2025-08-23-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-23-1/</guid><description>Native‑like swipeable sheets on the web</description><pubDate>Sat, 23 Aug 2025 17:53:44 GMT</pubDate></item><item><title>use-stick-to-bottom</title><link>https://alexcarpenter.me/notes/2025-08-18-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-18-2/</guid><description>A lightweight React Hook intended mainly for AI chat applications, for smoothly sticking to bottom of messages.</description><pubDate>Mon, 18 Aug 2025 21:20:03 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2025-08-18-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-18-1/</guid><pubDate>Mon, 18 Aug 2025 18:56:44 GMT</pubDate><content:encoded>&lt;p&gt;Ensure the trailing icon never orphans itself on to a new line. Paired with the icon alignment technique I &lt;a href=&quot;/notes/2025-08-14-1&quot;&gt;shared last week&lt;/a&gt; to vertically center it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p class=&quot;relative inline-block pr-[1.25em]&quot;&amp;gt;
  Lorem ipsum dolor sit amet consectetur adipisicing elit. Quia, ducimus
  &amp;lt;span class=&quot;absolute ml-[.25em] inline-flex h-[1lh] items-center&quot;&amp;gt;
    &amp;lt;Icon name=&quot;arrow-up-right&quot; class=&quot;size-[1em]&quot; /&amp;gt;
  &amp;lt;/span&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Picked this tip of from &lt;a href=&quot;https://x.com/JohnPhamous&quot;&gt;John Phamous&lt;/a&gt; some time ago.&lt;/p&gt;
</content:encoded></item><item><title>How to properly align icons within list items</title><link>https://alexcarpenter.me/notes/2025-08-14-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-14-1/</guid><pubDate>Thu, 14 Aug 2025 19:49:05 GMT</pubDate><content:encoded>&lt;p&gt;My current favorite approach to building bullet proof icon alignment within list items. Ensures icons are always vertically aligned to the first line of text, and ensures the icons do not shrink when text wraps to two lines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li class=&quot;flex gap-2&quot;&amp;gt;
    &amp;lt;span class=&quot;flex h-[1lh] items-center&quot;&amp;gt;
      &amp;lt;Icon class=&quot;size-[1em] flex-none&quot; name=&quot;badge-check&quot; /&amp;gt;
    &amp;lt;/span&amp;gt;
    List item 2 that is longer than the others and wraps to two lines
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this approach, you can apply a font size to the list item, and the icon will scale accordingly.&lt;/p&gt;
</content:encoded></item><item><title>We shipped a shadcn/ui registry</title><link>https://alexcarpenter.me/notes/2025-08-13-3/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-13-3/</guid><pubDate>Wed, 13 Aug 2025 20:34:12 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;npx shadcn@latest add https://clerk.com/r/nextjs-quickstart.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This single command will install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App layout with ClerkProvider and theme integration&lt;/li&gt;
&lt;li&gt;Sign-in and sign-up pages with catch-all routes&lt;/li&gt;
&lt;li&gt;Clerk middleware for route protection&lt;/li&gt;
&lt;li&gt;Header component with authentication buttons&lt;/li&gt;
&lt;li&gt;Theme provider for dark/light mode support&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-08-13-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-13-2/</guid><pubDate>Wed, 13 Aug 2025 12:10:08 GMT</pubDate><content:encoded>&lt;p&gt;For when &lt;code&gt;&amp;lt;mark /&amp;gt;&lt;/code&gt; elements wrap to multiple lines, use &lt;code&gt;box-decoration-break: clone;&lt;/code&gt; to render each fragment with their own specified border, padding, and margin.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mark {
  box-decoration-break: clone;
  padding-inline: 0.25rem;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-08-13-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-13-1/</guid><pubDate>Wed, 13 Aug 2025 11:54:31 GMT</pubDate><content:encoded>&lt;p&gt;Using &lt;code&gt;background-repeat: round;&lt;/code&gt; to get that repeated dot background to fit perfectly across differing viewport widths.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;div {
  background-image: radial-gradient(red 1px, transparent 1.3px);
  background-size: 24px 24px;
  background-position: 0 0;
  background-repeat: round;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-08-05-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-08-05-1/</guid><pubDate>Tue, 05 Aug 2025 23:26:48 GMT</pubDate><content:encoded>&lt;p&gt;Had a quick chat with Hamed about the recent work we have done to improve the customization of Clerk UI components. Watch it on &lt;a href=&quot;https://www.youtube.com/watch?v=0SWck1H3XSg&quot;&gt;YouTube&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-07-29-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-29-1/</guid><pubDate>Tue, 29 Jul 2025 12:25:58 GMT</pubDate><content:encoded>&lt;p&gt;Your component library ships bundled CSS via CSS-in-JS. You want folks to opt in to being able to toggle between light/dark mode but you don’t know how they are handling toggling between light/dark.&lt;/p&gt;
&lt;p&gt;Use CSS var toggle hack?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Component library styles */
:root {
  --dark-mode: ;
}

button {
  padding: 1rem;
  background-color: var(--dark-mode, white) black;
  color: var(--dark-mode, black) white;
}

/* User styles turn on dark mode */
.dark {
  --dark-mode: initial;
}

@media (prefers-color-scheme: dark) {
  :root {
    --dark-mode: initial;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the user the ability to enable dark mode based on how they have their app configured. @⁠media query, class, data-attr, etc.&lt;/p&gt;
&lt;p&gt;They can even be very targeted on which components this is enabled for.&lt;/p&gt;
</content:encoded></item><item><title>shadcn/ui theme compatibility is now available</title><link>https://alexcarpenter.me/notes/2025-07-23-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-23-1/</guid><pubDate>Wed, 23 Jul 2025 15:44:32 GMT</pubDate><content:encoded>&lt;p&gt;We shipped a new Clerk theme based on shadcn/ui that styles Clerk’s components according to your shadcn/ui theme.&lt;/p&gt;
</content:encoded></item><item><title>Write like you talk</title><link>https://alexcarpenter.me/notes/2025-07-21-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-21-1/</guid><pubDate>Mon, 21 Jul 2025 12:37:45 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Here’s a simple trick for getting more people to read what you write: write in spoken language.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Chase McCoy&apos;s personal website</title><link>https://alexcarpenter.me/notes/2025-07-18-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-18-2/</guid><pubDate>Fri, 18 Jul 2025 12:18:50 GMT</pubDate></item><item><title>Jonas Brinkhoff&apos;s personal website</title><link>https://alexcarpenter.me/notes/2025-07-18-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-18-1/</guid><pubDate>Fri, 18 Jul 2025 12:16:47 GMT</pubDate></item><item><title>Building your ideas with Claude Code and Figma MCP with Dan Hollick</title><link>https://alexcarpenter.me/notes/2025-07-17-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-17-2/</guid><pubDate>Fri, 18 Jul 2025 00:57:35 GMT</pubDate></item><item><title>Agentic Engineering</title><link>https://alexcarpenter.me/notes/2025-07-17-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-17-1/</guid><description>Combining human craftsmanship with AI tools to build better software.</description><pubDate>Fri, 18 Jul 2025 00:49:48 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Software development is changing and we find ourselves at a convergence. Between the extremes of technological zealotry (“all code will be AI-generated”) and dismissive skepticism (“AI-generated code is garbage”) lies a more practical and nuanced approach—one that is ours to discover together.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Clerk CSS variables are now available!</title><link>https://alexcarpenter.me/notes/2025-07-15-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-15-1/</guid><pubDate>Tue, 15 Jul 2025 19:21:54 GMT</pubDate><content:encoded>&lt;p&gt;Theme your Clerk components directly from your CSS files where your design tokens live – no more CSS-in-JS required.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --clerk-color-primary: #6d47ff;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We learned from our own experience: the variables appearance option had limited adoption because it was hard to integrate with existing design systems. Even we had to use workarounds with elements prop + Tailwind classes in our dashboard.&lt;/p&gt;
&lt;p&gt;Now you can theme components where your tokens are already defined. Plus we’ve improved variable naming and added new ones like &lt;code&gt;colorRing&lt;/code&gt;, &lt;code&gt;colorMuted&lt;/code&gt;, and &lt;code&gt;colorShadow&lt;/code&gt; for more flexible theming.&lt;/p&gt;
</content:encoded></item><item><title>What&apos;s the &quot;Geometry&quot; of Colours?</title><link>https://alexcarpenter.me/notes/2025-07-11-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-11-1/</guid><pubDate>Fri, 11 Jul 2025 14:55:45 GMT</pubDate></item><item><title>culori</title><link>https://alexcarpenter.me/notes/2025-07-08-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-08-1/</guid><description>A comprehensive color library for JavaScript.</description><pubDate>Tue, 08 Jul 2025 12:36:45 GMT</pubDate></item><item><title>That boolean should probably be something else</title><link>https://alexcarpenter.me/notes/2025-07-02-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-02-1/</guid><pubDate>Wed, 02 Jul 2025 12:42:00 GMT</pubDate></item><item><title>apcach</title><link>https://alexcarpenter.me/notes/2025-07-01-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-07-01-1/</guid><description>JS color calculator for composing colors with consistent APCA contrast ratio.</description><pubDate>Tue, 01 Jul 2025 11:23:59 GMT</pubDate></item><item><title>keyux</title><link>https://alexcarpenter.me/notes/2025-06-27-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-27-1/</guid><description>JS library to improve keyboard UI of web apps</description><pubDate>Fri, 27 Jun 2025 11:36:28 GMT</pubDate></item><item><title>On moving from implicit to explicit coupling</title><link>https://alexcarpenter.me/notes/2025-06-24-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-24-1/</guid><pubDate>Tue, 24 Jun 2025 12:49:00 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2025-06-21-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-21-1/</guid><pubDate>Sat, 21 Jun 2025 11:59:56 GMT</pubDate><content:encoded>&lt;p&gt;Working on some updates to make it easier to theme Clerk components from your existing CSS variables.&lt;/p&gt;
&lt;p&gt;Generating a color palette using relative color syntax and color-mix.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
  --brand-color: oklch(49.1% 0.27 292.581);
}

@media (prefers-color-scheme: dark) {
  :root {
    --brand-color: oklch(54.1% 0.281 293.009);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ClerkProvider
  appearance={{
    variables: {
      colorPrimary: &quot;var(--brand-color)&quot;,
    },
  }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Handle all potential cases in a switch statement</title><link>https://alexcarpenter.me/notes/2025-06-18-4/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-18-4/</guid><pubDate>Wed, 18 Jun 2025 12:08:25 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;type Cat = { kind: &apos;cat&apos; }
type Dog = { kind: &apos;dog&apos; }
type Pet = Cat | Dog

function example(pet: Pet) {
  switch (pet.kind) {
    case: &apos;cat&apos;:
      return ...
    case: &apos;dog&apos;
      return ...
    default:
      pet satisfies never
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Tiny polyfill for CSS scroll driven animations</title><link>https://alexcarpenter.me/notes/2025-06-18-3/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-18-3/</guid><pubDate>Wed, 18 Jun 2025 12:04:20 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;let animationRange = [0, 62];

if (!CSS.supports(&quot;(animation-timeline: scroll())&quot;)) {
  let [start, end] = animationRange;
  let animations = header.getAnimations();
  let onScroll = () =&amp;gt; {
    // Calculate animation time based on percentage of animationRange * duration.
    let time =
      Math.max(0, Math.min(end, window.scrollY - start) / (end - start)) * 1000;
    for (let animation of animations) {
      animation.currentTime = time;
    }
  };

  window.addEventListener(&quot;scroll&quot;, onScroll, { passive: true });
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>The Prettify Helper</title><link>https://alexcarpenter.me/notes/2025-06-18-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-18-2/</guid><pubDate>Wed, 18 Jun 2025 11:55:18 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;The Prettify helper is a utility type that takes an object type and makes the hover overlay more readable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;type Prettify&amp;lt;T&amp;gt; = {
  [K in keyof T]: T[K];
} &amp;amp; {};
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-06-18-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-06-18-1/</guid><pubDate>Wed, 18 Jun 2025 11:43:51 GMT</pubDate><content:encoded>&lt;p&gt;Automatic foreground color contrast based on the provided background color.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;button {
  --background: black;
  --foreground: color(
    from var(--background) xyz round(up, min(1, max(0, 0.18 - y)))
      round(up, min(1, max(0, 0.18 - y))) round(up, min(1, max(0, 0.18 - y)))
  );

  background-color: var(--background);
  color: var(--foreground);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;via &lt;a href=&quot;https://blog.damato.design/posts/css-only-contrast/&quot;&gt;blog.damato.design&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Evil Martions Harmonizer</title><link>https://alexcarpenter.me/notes/2025-05-26-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-05-26-1/</guid><pubDate>Mon, 26 May 2025 16:25:57 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Harmonizer is a tool for generating accessible, consistent color palettes for user interfaces. Using the OKLCH color model and APCA contrast formula, Harmonizer helps you create color palettes with consistent chroma and contrast across all levels and hues.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>never just</title><link>https://alexcarpenter.me/notes/2025-05-14-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-05-14-1/</guid><pubDate>Wed, 14 May 2025 13:10:48 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;it’s never just that simple&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>CSS Spring Easing Generator</title><link>https://alexcarpenter.me/notes/2025-05-02-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-05-02-1/</guid><pubDate>Fri, 02 May 2025 11:32:39 GMT</pubDate><content:encoded>&lt;p&gt;Also the companion &lt;a href=&quot;https://tailwindcss-spring.kvin.me/&quot;&gt;tailwindcss-spring&lt;/a&gt; plugin.&lt;/p&gt;
</content:encoded></item><item><title>useMaskedScroll hook</title><link>https://alexcarpenter.me/notes/2025-05-01-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-05-01-1/</guid><pubDate>Thu, 01 May 2025 12:19:09 GMT</pubDate><content:encoded>&lt;p&gt;Very nice scroll mask implementation using registered @ properties by &lt;a href=&quot;https://x.com/sambecker&quot;&gt;Sam Becker&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-04-30-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-30-1/</guid><pubDate>Wed, 30 Apr 2025 12:04:10 GMT</pubDate><content:encoded>&lt;p&gt;Not uncommon to see folks add form submit handlers on the submit buttons click event vs on the forms submit handler. The problem is this prevents users from being able to fill out the form and submit it solely from the keyboard.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form&amp;gt;
  &amp;lt;button onClick={handleSubmit} /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, apply the &lt;code&gt;handleSubmit&lt;/code&gt; function to the form &lt;code&gt;onSubmit&lt;/code&gt; handler. This ensures the form can be submitted when the user hits the &lt;kbd&gt;return&lt;/kbd&gt; key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form onSubmit={handleSubmit}&amp;gt;
  &amp;lt;button type=&quot;submit&quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If for whatever reason your button needs to live outside of the form element, likely in a dialog situation, you can ensure the same functionality by passing the forms id to the button via the &lt;code&gt;form&lt;/code&gt; attribute as shown below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form id=&quot;contactForm&quot; onSubmit={handleSubmit}&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;button form=&quot;contactForm&quot; type=&quot;submit&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Adaptive dotted pattern</title><link>https://alexcarpenter.me/notes/2025-04-19-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-19-2/</guid><pubDate>Sat, 19 Apr 2025 11:29:11 GMT</pubDate></item><item><title>The Wet Codebase</title><link>https://alexcarpenter.me/notes/2025-04-19-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-19-1/</guid><pubDate>Sat, 19 Apr 2025 11:24:05 GMT</pubDate></item><item><title>OKLCH in CSS: why we moved from RGB and HSL</title><link>https://alexcarpenter.me/notes/2025-04-18-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-18-2/</guid><pubDate>Fri, 18 Apr 2025 12:02:53 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2025-04-18-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-18-1/</guid><pubDate>Fri, 18 Apr 2025 11:59:33 GMT</pubDate><content:encoded>&lt;p&gt;Quick little improvement for wrapping highlighted text. Make use of &lt;code&gt;box-decoration-break: clone;&lt;/code&gt; to ensure elements fragments break across lines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mark {
  box-decoration-break: clone;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View demo on &lt;a href=&quot;https://x.com/alexcarp_me/status/1821533842228089056/video/1&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-04-17-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-17-1/</guid><pubDate>Thu, 17 Apr 2025 12:18:43 GMT</pubDate><content:encoded>&lt;p&gt;Quick little improvement for elements that render dark even in light mode and have overflow, force the color-scheme to dark to blend the native form controls a bit better.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pre {
  color-scheme: dark;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View demo on &lt;a href=&quot;https://x.com/alexcarp_me/status/1823406174034649137/video/1&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-04-16-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-16-1/</guid><pubDate>Wed, 16 Apr 2025 12:45:17 GMT</pubDate><content:encoded>&lt;p&gt;Stop vertically aligning your checkboxes with &lt;code&gt;center&lt;/code&gt;. Instead use &lt;code&gt;baseline&lt;/code&gt; to keep it aligned with the first line of the label text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;label {
  display: flex;
  align-items: center; // [!code --]
  align-items: baseline; // [!code ++]
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><link>https://alexcarpenter.me/notes/2025-04-15-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-15-1/</guid><pubDate>Tue, 15 Apr 2025 12:20:41 GMT</pubDate><content:encoded>&lt;p&gt;This was a fun layout challenge for some dashboard UI we’ve been working on at Clerk. The structure of the layout contains your typical sidebar and content elements that live side by side.&lt;/p&gt;
&lt;p&gt;Simplified markup example of what we are working with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;header&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;nav&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;div class=&quot;layout&quot;&amp;gt;
  &amp;lt;div class=&quot;sidebar&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;content&quot;&amp;gt;
    &amp;lt;div class=&quot;data&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The data element is a large table which i’ve hard coded to cause an overflow that needs to be able to scroll vertically and horizontally but we wanted to make sure that its scroll bars were always within the viewport.&lt;/p&gt;
&lt;p&gt;To accomplish this we can make use of &lt;code&gt;contain: size;&lt;/code&gt; to ensure the content overflows within its container and not cause the page to need to scroll.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.header {
  width: 100%;
  height: 56px;
}

.nav {
  width: 100%;
  height: 56px;
  position: sticky;
}

.layout {
  flex: 1;
  display: grid;
  grid-template-columns: 360px 1fr;
}

.sidebar {...}

.content {
  overflow: auto;
  contain: size;
}

/* This is simply to cause an overflow to demonstrate the table size */
.data {
  width: 200vw;
  height: 200vh;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View the demo CodePen &lt;a href=&quot;https://codepen.io/alexcarpenter/pen/emmOLoN?editors=0100&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Create a list of links from an array using Intl.ListFormat with formatToParts</title><link>https://alexcarpenter.me/notes/2025-04-14-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-14-2/</guid><pubDate>Tue, 15 Apr 2025 00:50:41 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;const tags = [&quot;HTML&quot;, &quot;CSS&quot;, &quot;JavaScript&quot;];

{
  new Intl.ListFormat(&quot;en-US&quot;).formatToParts(tags).map(({ type, value }) =&amp;gt; {
    if (type === &quot;element&quot;) {
      return &amp;lt;a href={`/${slugify(value)}`}&amp;gt;{value}&amp;lt;/a&amp;gt;;
    }
    return value;
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which returns the following markup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;/html&quot;&amp;gt;HTML&amp;lt;/a&amp;gt;, &amp;lt;a href=&quot;/css&quot;&amp;gt;CSS&amp;lt;/a&amp;gt;, and
&amp;lt;a href=&quot;/javascript&quot;&amp;gt;JavaScript&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Animate height from 0 to auto</title><link>https://alexcarpenter.me/notes/2025-04-14-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-14-1/</guid><pubDate>Mon, 14 Apr 2025 16:41:36 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;.element {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.5s ease-in-out;
}

.element.open {
  grid-template-rows: 1fr;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;References:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.stefanjudis.com/snippets/how-to-animate-height-with-css-grid/&quot;&gt;How to animate an element’s height with CSS grid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/animating-css-grid-how-to-examples/&quot;&gt;Animating CSS Grid (How To + Examples) &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Write code that is easy to delete, not easy to extend</title><link>https://alexcarpenter.me/notes/2025-04-13-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-04-13-1/</guid><pubDate>Mon, 14 Apr 2025 00:14:22 GMT</pubDate></item><item><title>Naming things in design systems–and why it’s the worst</title><link>https://alexcarpenter.me/notes/2025-03-22-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-03-22-1/</guid><pubDate>Sat, 22 Mar 2025 23:25:15 GMT</pubDate></item><item><title>Sanding UI</title><link>https://alexcarpenter.me/notes/2025-02-25-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-02-25-1/</guid><pubDate>Tue, 25 Feb 2025 23:26:08 GMT</pubDate></item><item><link>https://alexcarpenter.me/notes/2025-02-19-2/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-02-19-2/</guid><pubDate>Wed, 19 Feb 2025 21:37:34 GMT</pubDate><content:encoded>&lt;p&gt;React Aria exposing state through the className is super handy. Here we’re using the placement returned to define a Tailwind CSS variable which we can then use in Motion to define its animation direction. View sandbox example &lt;a href=&quot;https://codesandbox.io/p/sandbox/zwfndk&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;TooltipTrigger isOpen={open} onOpenChange={setOpen}&amp;gt;
  &amp;lt;Button&amp;gt;Trigger&amp;lt;/Button&amp;gt;
  &amp;lt;AnimatePresence&amp;gt;
    {open ? (
      &amp;lt;MotionTooltip
        className={({ placement }) =&amp;gt;
          cx({
            &quot;[--y:4px]&quot;: placement === &quot;top&quot;,
            &quot;[--y:-4px]&quot;: placement === &quot;bottom&quot;,
          })
        }
        offset={6}
        initial={{ opacity: 0, y: &quot;var(--y)&quot; }}
        animate={{ opacity: 1, y: 0 }}
      &amp;gt;
        Content
      &amp;lt;/MotionTooltip&amp;gt;
    ) : null}
  &amp;lt;/AnimatePresence&amp;gt;
&amp;lt;/TooltipTrigger&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>The &quot;everything bagel&quot; of components</title><link>https://alexcarpenter.me/notes/2025-02-17-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2025-02-17-1/</guid><pubDate>Mon, 17 Feb 2025 23:21:04 GMT</pubDate></item><item><title>Just use children</title><link>https://alexcarpenter.me/notes/2024-12-05-1/</link><guid isPermaLink="true">https://alexcarpenter.me/notes/2024-12-05-1/</guid><pubDate>Thu, 05 Dec 2024 23:25:15 GMT</pubDate></item></channel></rss>