<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ray&apos;s TIL</title><description>Small things I&apos;ve picked up in my day-to-day life as a frontend engineer, inspired by Stefan Judis</description><link>https://itsmeray.com/</link><language>en-us</language><item><title>Notes on Writing Well</title><link>https://itsmeray.com/til/notes-on-writing-well/</link><guid isPermaLink="true">https://itsmeray.com/til/notes-on-writing-well/</guid><pubDate>Mon, 12 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Michael Nielsen keeps a set of rough &lt;a href=&quot;https://github.com/mnielsen/notes-on-writing/blob/master/notes_on_writing.md&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;working notes on writing&lt;/a&gt;. He calls them notes, not polished prose, but they’re full of ideas I keep coming back to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good writing is good psychology.&lt;/strong&gt; Rules like “avoid adverbs” or “omit needless words” usually come without a reason. Nielsen’s point is that good writing isn’t a property of the text. It’s a property of the relationship between the reader and the text, so useful advice is about how to change that relationship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One purpose.&lt;/strong&gt; A good piece has a single, sharp purpose, and every part of it, even the digressions, should serve that purpose. The failure mode he calls &lt;em&gt;discursive disease&lt;/em&gt;: you’re writing about gardens, then you add that good thought you had about rainforests, and now the piece is sort of about both and really about neither. His fix is to write the purpose at the top of the draft and reread it every time you sit down. Anything that doesn’t fit gets filed away for another piece, the way Aaron Sorkin’s leftover material from &lt;em&gt;The American President&lt;/em&gt; went on to become &lt;em&gt;The West Wing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It’s hard to write the truth.&lt;/strong&gt; Early drafts are full of the written version of “you know what I mean”. The only fix he knows is to revise over and over, asking of each sentence whether it can be sharper, and whether you actually believe it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Beware words you wouldn’t say out loud.&lt;/strong&gt; They’re usually there to sound authoritative, which serves the writer, not the reader.&lt;/p&gt;
&lt;p&gt;A few smaller ones worth stealing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Write one true sentence.&lt;/strong&gt; Hemingway’s way out of being stuck. Start with the simplest true thing you know and go from there.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop when you know what comes next.&lt;/strong&gt; Also Hemingway. If you end each session mid-flow, you’ll never start the next one stuck.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brainstorm ten opening sentences.&lt;/strong&gt; When the first line won’t come, don’t settle for a mediocre one. Write ten quickly, then judge them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Start as late as possible.&lt;/strong&gt; Ask whether the opening sentence could begin further in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t announce that something is interesting.&lt;/strong&gt; “It’s interesting to consider…” tells the reader how to feel instead of making them feel it, and quietly suggests the topic sounds boring. Just say “Consider…”, then make it interesting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“Very” is the worst adverb.&lt;/strong&gt; “She looked very angry” loses little without it. Where the intensity matters, swap the phrase for something specific: “Jake would very much like lunch” becomes “Jake was ravenous for lunch”.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>writing</category></item><item><title>Scroll Animation Terms</title><link>https://itsmeray.com/til/scroll-animation-terms/</link><guid isPermaLink="true">https://itsmeray.com/til/scroll-animation-terms/</guid><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;If you struggle to describe scroll animations in Claude or Codex, these commonly used terms can help. Save this for later.&lt;/p&gt;— Gabriel (@gabriell_lab) &lt;a href=&quot;https://x.com/gabriell_lab/status/2107443057805476100&quot;&gt;October 6, 2026&lt;/a&gt;&lt;/blockquote&gt; 
&lt;p&gt;“Make it animate when I scroll” can mean at least six different things. Naming the right one gets you the right result on the first try, whether you’re prompting an AI or briefing a designer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scroll-triggered&lt;/strong&gt;: the animation plays once when an element enters the viewport, then runs on its own clock. Scrolling starts it but doesn’t control it. Usually built with &lt;code&gt;IntersectionObserver&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scroll-linked&lt;/strong&gt;: the animation’s progress is tied directly to the scroll position, so it scrubs forwards and backwards as you scroll. A reading progress bar is the classic example. CSS can now do this natively with &lt;code&gt;animation-timeline: scroll()&lt;/code&gt; or &lt;code&gt;view()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallax&lt;/strong&gt;: layers move at different speeds from the page, so the background drifts slower than the foreground and fakes depth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sticky&lt;/strong&gt;: an element scrolls normally until it hits an edge, then stays pinned there until its container scrolls past. Think of the A, B, C letter headers in a contacts list. It’s just &lt;code&gt;position: sticky; top: 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scroll snap&lt;/strong&gt;: scrolling settles on set points instead of stopping anywhere, like a full-screen slide deck. Set &lt;code&gt;scroll-snap-type: y mandatory&lt;/code&gt; on the container and &lt;code&gt;scroll-snap-align: start&lt;/code&gt; on each child.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal scroll&lt;/strong&gt;: content moves sideways. This is either a plain &lt;code&gt;overflow-x&lt;/code&gt; carousel, or a pinned section that turns your vertical scroll into sideways movement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The pair that gets mixed up the most is &lt;strong&gt;triggered vs. linked&lt;/strong&gt;. If the animation should finish by itself once it starts, it’s triggered. If it should follow your scroll, including when you scroll back up, it’s linked.&lt;/p&gt;</content:encoded><category>css</category><category>frontend</category></item><item><title>Push vs. Pull vs. Merge Requests</title><link>https://itsmeray.com/til/pull-push-merge-requests/</link><guid isPermaLink="true">https://itsmeray.com/til/pull-push-merge-requests/</guid><pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;callout callout--note&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;note&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;p class=&quot;callout__title&quot;&gt;TLDR&lt;/p&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;“Pull request” is a leftover from the pre-GitHub days, when you’d email a maintainer asking them to &lt;em&gt;pull&lt;/em&gt; your branch (and tell them where to find your fork). GitHub kept the name; GitLab later chose “merge request” because merging is the action you actually care about. The terminology is a social artifact, not a technical one.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;tbh i still don’t get why it’s called a pull request instead of a push request&lt;/p&gt;— ange (˶♡ᴗ♡) (@angehyc) &lt;a href=&quot;https://x.com/angehyc/status/2067835645155009011?ref_src=twsrc%5Etfw&quot;&gt;June 19, 2026&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;Like any other student, I was first acquainted with Git through GitHub. Like the OP of the tweet above, I wondered the same thing but never really put much thought into it.&lt;/p&gt;
&lt;p&gt;That changed when I moved to PayNet and started using GitLab. Not only did they use different terminology (merge request, or MR), it also rekindled my curiosity about how the terms differ and why they exist in the first place.&lt;/p&gt;
&lt;p&gt;Thankfully, the thread below put the question to rest:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;As someone who used GitHub for years, went to work at GitLab, and was yelled at dozens of times for calling it a pull request (not a merge request) I know a lot about this topic &lt;a href=&quot;https://t.co/vEbhv32g1x&quot;&gt;https://t.co/vEbhv32g1x&lt;/a&gt;&lt;/p&gt;— Phil Zona (@philzona) &lt;a href=&quot;https://x.com/philzona/status/2067959899435221418?ref_src=twsrc%5Etfw&quot;&gt;June 19, 2026&lt;/a&gt;&lt;/blockquote&gt; 
&lt;p&gt;Before GitHub and GitLab, most dev work involving Git happened over mailing lists and self-hosted Git servers. When you wanted to land a change, you had to actively reach out to the maintainer.&lt;/p&gt;
&lt;p&gt;What you’d ask them: &lt;em&gt;“Hey, can you pull my branch into master?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That message &lt;strong&gt;was&lt;/strong&gt; the pull request. It was simply the abstraction people had at the time, and GitHub named its action after the workflow everyone was already using.&lt;/p&gt;
&lt;p&gt;GitLab came along a couple of years later with “merge request” (MR), which in my honest opinion is more &lt;em&gt;technically&lt;/em&gt; accurate. The action contributors actually care about is merging, not just pulling and reviewing.&lt;/p&gt;
&lt;p&gt;But GitHub’s name makes sense too once you remember Git is distributed, and so were forks back then. Part of a pull request was telling the maintainer &lt;strong&gt;where to find your fork&lt;/strong&gt; and &lt;em&gt;how&lt;/em&gt; to pull your branch, because GitHub didn’t yet exist to host it for you.&lt;/p&gt;
&lt;p&gt;The more you know. ¯_(ツ)_/¯&lt;/p&gt;</content:encoded><category>tools</category></item><item><title>Core Web Vitals: LCP, CLS &amp; INP</title><link>https://itsmeray.com/til/core-web-vitals/</link><guid isPermaLink="true">https://itsmeray.com/til/core-web-vitals/</guid><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Core Web Vitals are the three metrics Google uses to measure real-world user experience. They directly influence search rankings and are what actually matters when someone asks “is this site fast?”&lt;/p&gt;

































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Metric&lt;/th&gt;&lt;th&gt;What it measures&lt;/th&gt;&lt;th&gt;Good&lt;/th&gt;&lt;th&gt;Poor&lt;/th&gt;&lt;th&gt;Common fixes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;LCP&lt;/strong&gt; (Largest Contentful Paint)&lt;/td&gt;&lt;td&gt;Time until the largest visible element renders&lt;/td&gt;&lt;td&gt;≤ 2.5s&lt;/td&gt;&lt;td&gt;&gt; 4s&lt;/td&gt;&lt;td&gt;Preload LCP image, inline critical CSS, use &lt;code&gt;fetchpriority=&quot;high&quot;&lt;/code&gt;, fast server response&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CLS&lt;/strong&gt; (Cumulative Layout Shift)&lt;/td&gt;&lt;td&gt;How much the layout shifts unexpectedly during load&lt;/td&gt;&lt;td&gt;≤ 0.1&lt;/td&gt;&lt;td&gt;&gt; 0.25&lt;/td&gt;&lt;td&gt;Set explicit &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; on images, reserve space for dynamic content, use &lt;code&gt;font-display: swap&lt;/code&gt; with &lt;code&gt;size-adjust&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;INP&lt;/strong&gt; (Interaction to Next Paint)&lt;/td&gt;&lt;td&gt;Worst-case latency from interaction to visual response&lt;/td&gt;&lt;td&gt;≤ 200ms&lt;/td&gt;&lt;td&gt;&gt; 500ms&lt;/td&gt;&lt;td&gt;Break up long tasks with &lt;code&gt;scheduler.yield()&lt;/code&gt;, debounce handlers, move heavy work to web workers&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Google measures these on real Chrome users via the Chrome UX Report (CrUX). Your Lighthouse score in DevTools is lab data &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; useful for debugging, but the field data from CrUX is what determines your pass/fail status in Search Console.&lt;/p&gt;
&lt;div class=&quot;callout callout--info&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;info&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;INP replaced FID (First Input Delay) in March 2024. Where FID only measured the &lt;em&gt;first&lt;/em&gt; interaction’s input delay, INP tracks &lt;strong&gt;all&lt;/strong&gt; interactions throughout the page’s lifecycle and reports roughly the p98 worst case &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; a much better reflection of how responsive the page actually feels.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><category>performance</category><category>frontend</category></item><item><title>Partial Hydration &amp; Islands Architecture 101</title><link>https://itsmeray.com/til/islands-architecture/</link><guid isPermaLink="true">https://itsmeray.com/til/islands-architecture/</guid><pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Modern JavaScript frameworks ship a lot of JavaScript. But most websites &lt;em&gt;don’t need&lt;/em&gt; a lot of JavaScript.&lt;/p&gt;
&lt;p&gt;On one end of the spectrum, you have dashboards, SaaS apps, and real-time editors &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; deeply interactive pages where JS is the product. On the other end, documentation, blogs, and marketing pages that are pure content and need &lt;strong&gt;zero JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But there’s a whole category of sites in between that need &lt;em&gt;some&lt;/em&gt; interactivity, just not a lot:&lt;/p&gt;
&lt;div align=&quot;”center”&quot;&gt;
  &lt;img src=&quot;https://imgur.com/iZYvfrz.png&quot; alt=&quot;Goldilocks zone for JS interactivity&quot;&gt;
&lt;/div&gt;
&lt;p&gt;These “goldilocks” sites are awkward for traditional frameworks. You can’t fully statically generate them because &lt;em&gt;some&lt;/em&gt; parts need to be interactive, but shipping an entire framework runtime for a theme toggle and a few animated icons feels like overkill.&lt;/p&gt;
&lt;p&gt;The root of the problem is &lt;strong&gt;hydration&lt;/strong&gt;. When a server-rendered page reaches the browser, the framework loads and &lt;a href=&quot;https://itsmeray.com/thoughts/rendering-strategies-explained#wtf-is-hydration&quot;&gt;hydrates&lt;/a&gt; the entire component tree &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; walking every node, attaching every listener &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; even for the 90% of the page that’s static content and will never respond to a click. That’s a lot of JavaScript downloaded and executed for nothing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Partial hydration&lt;/strong&gt; flips the model. Instead of hydrating everything and carving out static parts, you start from zero JavaScript and opt &lt;em&gt;in&lt;/em&gt; to interactivity only where you need it. The rest of the page stays as pure, server-rendered HTML with no runtime overhead.&lt;/p&gt;
&lt;p&gt;This is the idea behind the &lt;strong&gt;islands architecture&lt;/strong&gt;, a term coined by Etsy’s frontend architect Katie Sylor-Miller and later popularised by Preact creator &lt;a href=&quot;https://jasonformat.com/islands-architecture/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jason Miller&lt;/a&gt;. Think of your page as an ocean of static HTML with small, independent “islands” of interactivity scattered across it. Each island hydrates on its own, in isolation, without knowing about or depending on the others.&lt;/p&gt;
&lt;h2 id=&quot;how-astro-implements-it&quot;&gt;How Astro implements it&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Astro&lt;/a&gt; is probably the most well-known framework built around this model. By default, Astro components render to &lt;strong&gt;static HTML at build time with zero client-side JavaScript&lt;/strong&gt;. When you need interactivity, you explicitly opt in using &lt;code&gt;client:*&lt;/code&gt; directives on a per-component basis:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;// Everything here runs at build time only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#0DB9D7&quot;&gt; Header&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;../components/Header.astro&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;     // static, no JS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#0DB9D7&quot;&gt; SocialLinks&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;../components/SocialLinks&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;  // React component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#0DB9D7&quot;&gt; GitHubCalendar&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;../components/GitHubCalendar&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt; // React component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;&amp;#x3C;!-- Pure HTML, zero JS --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#EA76CB;--shiki-dark:#0DB9D7&quot;&gt;Header&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;&amp;#x3C;!-- These become interactive islands --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#EA76CB;--shiki-dark:#0DB9D7&quot;&gt;SocialLinks&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; client:idle&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#EA76CB;--shiki-dark:#0DB9D7&quot;&gt;GitHubCalendar&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; client:only&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;react&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;client:*&lt;/code&gt; directives control &lt;strong&gt;when&lt;/strong&gt; each island hydrates:&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Directive&lt;/th&gt;&lt;th&gt;When it hydrates&lt;/th&gt;&lt;th&gt;Good for&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;client:load&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Immediately on page load&lt;/td&gt;&lt;td&gt;Critical interactive UI that must work right away&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;client:idle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;After the page is done loading (browser idle)&lt;/td&gt;&lt;td&gt;Non-critical interactive components like theme toggles, animated icons&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;client:visible&lt;/code&gt;&lt;/td&gt;&lt;td&gt;When the component scrolls into view&lt;/td&gt;&lt;td&gt;Below-the-fold content like pagination arrows, newsletter forms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;client:only=&quot;react&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Client-side only, never SSR’d&lt;/td&gt;&lt;td&gt;Components that depend entirely on browser APIs (e.g. GitHub contribution calendar)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;client:media=&quot;(max-width: 768px)&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;When a CSS media query matches&lt;/td&gt;&lt;td&gt;Mobile-only interactive components&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;how-this-site-uses-it&quot;&gt;How this site uses it&lt;/h2&gt;
&lt;p&gt;This site you’re reading is built with Astro. The vast majority of it &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; the sidebar, the blog content, the navigation, all the layout &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; renders to static HTML at build time. No framework, no virtual DOM, no JavaScript. It’s just HTML and CSS.&lt;/p&gt;
&lt;p&gt;The interactive islands are small and deliberate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Animated icons&lt;/strong&gt; (&lt;code&gt;client:idle&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; the little SVG icons that animate on hover throughout the site. They hydrate after the page settles so they don’t compete with the initial render.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme and notification toggles&lt;/strong&gt; (&lt;code&gt;client:idle&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; the brightness/moon icons and bell icons in the header. Interactive but not urgent.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social links&lt;/strong&gt; (&lt;code&gt;client:idle&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; React components that handle hover animations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub contribution calendar&lt;/strong&gt; (&lt;code&gt;client:only=&quot;react&quot;&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; fully client-rendered because it depends on browser APIs and fetches data at runtime. No server-side HTML is even attempted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pagination chevrons and newsletter form&lt;/strong&gt; (&lt;code&gt;client:visible&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; only hydrate when you scroll down to them. Why load JavaScript for something you might never see?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vinyl collection grid&lt;/strong&gt; (&lt;code&gt;client:idle&lt;/code&gt;) &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; the interactive filtering and sorting on the vinyls page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these islands loads and hydrates independently. If the GitHub calendar fails to load, the rest of the page is completely unaffected &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; it was never JavaScript to begin with.&lt;/p&gt;
&lt;h2 id=&quot;why-it-matters&quot;&gt;Why it matters&lt;/h2&gt;
&lt;p&gt;The performance difference is real. A traditional React SPA would ship the framework plus every component’s JavaScript to the browser, even for a page that’s 95% static prose. With islands, that same page ships close to &lt;strong&gt;zero JS&lt;/strong&gt; unless you explicitly need it.&lt;/p&gt;
&lt;p&gt;This is particularly noticeable on content-heavy sites like blogs and documentation. The content &lt;em&gt;is&lt;/em&gt; the product, and it doesn’t need reactivity. Islands architecture lets you get the DX of component-based frameworks (use React, Vue, Svelte &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; Astro doesn’t care) without paying the runtime cost for static content.&lt;/p&gt;
&lt;div class=&quot;callout callout--info&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;info&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;Islands architecture and partial hydration are closely related but not identical. &lt;strong&gt;Partial hydration&lt;/strong&gt; is the technique (hydrate only some components). &lt;strong&gt;Islands architecture&lt;/strong&gt; is the mental model (static ocean, interactive islands).&lt;/p&gt;&lt;p&gt;Astro uses partial hydration to implement the islands model. Other approaches to partial hydration exist too &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; React Server Components, for example, solve a similar problem but from within a single-framework paradigm rather than Astro’s framework-agnostic approach.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><category>frontend</category><category>architecture</category></item><item><title>Claude Code Hooks 101</title><link>https://itsmeray.com/til/claude-code-hooks/</link><guid isPermaLink="true">https://itsmeray.com/til/claude-code-hooks/</guid><pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is a suggestion. Claude reads it and follows it about 80% of the time. Hooks are different. They’re shell commands that fire automatically every time Claude does something, regardless of what the prompt says.&lt;/p&gt;
&lt;p&gt;Two hook types cover most workflows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/strong&gt; runs before the tool executes. Exit code &lt;code&gt;2&lt;/code&gt; blocks the action and sends your error message back to Claude so it can try a safer approach. Think of it as a bouncer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PostToolUse&lt;/code&gt;&lt;/strong&gt; runs after the tool completes. Good for formatting, linting, logging. Think of it as quality control on the assembly line.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hooks live in one of &lt;strong&gt;3 places&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.claude/settings.json         # project-level, committed to git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;~/.claude/settings.json       # user-level, all projects&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;.claude/settings.local.json   # local only, not committed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;block-dangerous-commands&quot;&gt;Block dangerous commands&lt;/h2&gt;
&lt;p&gt;Claude is powerful enough to run &lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, or &lt;code&gt;curl … | bash&lt;/code&gt;. It probably won’t, but “probably” isn’t good enough.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-title=&quot;.claude/hooks/block-dangerous.sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA76CB;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -euo&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt;jq&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;.tool_input.command // &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;dangerous_patterns&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;rm -rf&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;git reset --hard&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;git push.*--force&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;DROP TABLE&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;DROP DATABASE&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;curl.*|.*sh&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;wget.*|.*bash&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; pattern&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#89DDFF&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;dangerous_patterns&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#89DDFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;$cmd&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -qiE&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;$pattern&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;Blocked: &apos;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;$cmd&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;&apos; matches dangerous pattern &apos;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;$pattern&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;&apos;. Propose a safer alternative.&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; &gt;&amp;#x26;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;    exit&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt;done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit code &lt;code&gt;2&lt;/code&gt; is the key. It cancels the action and the error message goes back to Claude so it knows to try something safer. Exit &lt;code&gt;0&lt;/code&gt; means go ahead. Anything else logs a warning but doesn’t block.&lt;/p&gt;
&lt;h2 id=&quot;log-every-command&quot;&gt;Log every command&lt;/h2&gt;
&lt;p&gt;Appends every Bash command Claude runs to a timestamped audit trail.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-title=&quot;.claude/hooks/log-commands.sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EA76CB;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -euo&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt;jq&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;.tool_input.command // &quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;%s %s\n&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt;date&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#E0AF68&quot;&gt; -Is&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;$cmd&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; &gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt; .claude/command-log.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D20F39;--shiki-light-font-style:italic;--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit&quot;&gt;exit&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add &lt;code&gt;.claude/command-log.txt&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;. When something breaks, you have an exact record of what ran and when.&lt;/p&gt;
&lt;h2 id=&quot;auto-format-and-lint-after-every-edit&quot;&gt;Auto-format and lint after every edit&lt;/h2&gt;
&lt;p&gt;Rather than telling Claude “always run Prettier” in &lt;code&gt;CLAUDE.md&lt;/code&gt; and hoping it remembers, a &lt;code&gt;PostToolUse&lt;/code&gt; hook makes it unconditional.&lt;/p&gt;
&lt;p&gt;The hook receives the tool call as JSON on stdin, so &lt;code&gt;jq&lt;/code&gt; can pull out the file path:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot; data-title=&quot;.claude/settings.json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;hooks&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#0DB9D7&quot;&gt;PreToolUse&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7DCFFF&quot;&gt;matcher&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;Bash&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7DCFFF&quot;&gt;hooks&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;.claude/hooks/block-dangerous.sh&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;.claude/hooks/log-commands.sh&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;    ]&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#0DB9D7&quot;&gt;PostToolUse&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7DCFFF&quot;&gt;matcher&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;Write|Edit&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;        &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7DCFFF&quot;&gt;hooks&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;jq -r &apos;.tool_input.file_path&apos; | xargs npx prettier --write 2&gt;/dev/null; exit 0&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;npx eslint --fix $(jq -r &apos;.tool_input.file_path&apos;) 2&gt;&amp;#x26;1 | tail -10; exit 0&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;            &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#BB9AF7&quot;&gt;command&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;npm run test --silent 2&gt;&amp;#x26;1 | tail -5; exit 0&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prettier runs first, then ESLint with &lt;code&gt;--fix&lt;/code&gt;, then the test suite. The &lt;code&gt;tail -5&lt;/code&gt; keeps test output short so Claude sees “3 tests failed” rather than 200 lines. The &lt;code&gt;; exit 0&lt;/code&gt; on all three means failures are reported but never block the session. By the time you look at the code, it’s formatted, lint-clean, and you already know if tests are passing.&lt;/p&gt;</content:encoded><category>tooling</category></item><item><title>Speculation Rules API</title><link>https://itsmeray.com/til/speculation-rules-prerendering/</link><guid isPermaLink="true">https://itsmeray.com/til/speculation-rules-prerendering/</guid><pubDate>Sun, 29 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;prefetch-vs-prerender&quot;&gt;Prefetch vs. Prerender&lt;/h2&gt;
&lt;p&gt;By default the browser does nothing until you click, then kicks off the full sequence from scratch: DNS, TCP, TLS, HTML fetch, parse, JS execute, render. All of that happens &lt;em&gt;after&lt;/em&gt; you’ve committed to navigating.&lt;/p&gt;
&lt;p&gt;Both prefetch and prerender move that work earlier, but they stop at different points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prefetch&lt;/strong&gt; stops after the network. It downloads the HTML and sub-resources into cache, but the page isn’t parsed or rendered. You still pay the rendering cost on click, just not the network cost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerender&lt;/strong&gt; goes further. It fetches, parses, executes JS, lays out, and paints in a hidden background tab. When you click, the browser swaps the already-rendered page in.&lt;/p&gt;
&lt;h3 id=&quot;prefetch-strategies&quot;&gt;Prefetch strategies&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Strategy&lt;/th&gt;&lt;th&gt;Triggers on&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;tap&lt;/code&gt;&lt;/td&gt;&lt;td&gt;mousedown, just before click&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;hover&lt;/code&gt;&lt;/td&gt;&lt;td&gt;hover delay (~80ms)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;viewport&lt;/code&gt;&lt;/td&gt;&lt;td&gt;link scrolls into view&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;load&lt;/code&gt;&lt;/td&gt;&lt;td&gt;every link, immediately on page load&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;prerendering&quot;&gt;Prerendering&lt;/h3&gt;
&lt;p&gt;You can trigger it per-link with &lt;code&gt;rel=&quot;prerender&quot;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;prerender&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;/about&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The opposite end is lazy loading: defer everything until explicitly needed (&lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt;, dynamic imports, &lt;code&gt;IntersectionObserver&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&quot;callout callout--note&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;note&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;p class=&quot;callout__title&quot;&gt;TLDR&lt;/p&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;Prerender pays upfront. Lazy loading pays on demand.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-speculation-rules-api&quot;&gt;The Speculation Rules API&lt;/h2&gt;
&lt;p&gt;Chrome 109+ lets you control this with a JSON script tag:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;speculationrules&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;  {&quot;prerender&quot;:[{&quot;where&quot;:{&quot;href_matches&quot;:&quot;/*&quot;},&quot;eagerness&quot;:&quot;moderate&quot;}]}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;eagerness&lt;/code&gt; controls when the browser triggers prerendering:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Value&lt;/th&gt;&lt;th&gt;Triggers on&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;conservative&lt;/code&gt;&lt;/td&gt;&lt;td&gt;mousedown&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;moderate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;hover / focus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;eager&lt;/code&gt;&lt;/td&gt;&lt;td&gt;link in DOM&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;moderate&lt;/code&gt; is the right default. Hover is clear enough intent, and a wrong guess just wastes a bit of bandwidth.&lt;/p&gt;
&lt;h2 id=&quot;prefetch--prerender-compose-well&quot;&gt;Prefetch + prerender compose well&lt;/h2&gt;
&lt;p&gt;Astro’s &lt;code&gt;prefetch: { defaultStrategy: &apos;viewport&apos; }&lt;/code&gt; fires when a link scrolls into view, warming the cache early. By hover time the assets are already local, so the prerender finishes fast:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;link enters viewport → prefetch (network)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;             ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         user hovers → prerender (full render, from cache)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              ↓&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            click → instant swap&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;callout callout--info&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;info&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;&lt;strong&gt;Don’t prerender pages that run side effects on load&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; analytics hits, impression counters. The browser executes those during prerender, before the user has actually visited.&lt;/p&gt;&lt;p&gt;On this site view counts are incremented by a React component on mount, which only hydrates on real navigation, so it’s safe.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Other browsers silently ignore the script tag, so no broken behaviour as a fallback.&lt;/p&gt;</content:encoded><category>performance</category></item><item><title>CSS font units: em, rem, ex, ch, cap</title><link>https://itsmeray.com/til/css-font-units/</link><guid isPermaLink="true">https://itsmeray.com/til/css-font-units/</guid><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Five units tied to the current font, each measuring something different:&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;What it measures&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;em&lt;/code&gt;&lt;/td&gt;&lt;td&gt;font-size of the current element&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rem&lt;/code&gt;&lt;/td&gt;&lt;td&gt;font-size of the root (&lt;code&gt;&amp;#x3C;html&gt;&lt;/code&gt;) element&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ex&lt;/code&gt;&lt;/td&gt;&lt;td&gt;x-height &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; height of a lowercase “x”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;advance measure of the “0” glyph&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;cap&lt;/code&gt;&lt;/td&gt;&lt;td&gt;cap height &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; height of an uppercase letter&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;em&lt;/code&gt; compounds when nested. A &lt;code&gt;1.2em&lt;/code&gt; element inside another &lt;code&gt;1.2em&lt;/code&gt; element is &lt;code&gt;1.44×&lt;/code&gt; the root size. &lt;code&gt;rem&lt;/code&gt; skips all that &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; always anchored to &lt;code&gt;:root&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ch&lt;/code&gt; is the useful one people overlook. Setting &lt;code&gt;width: 60ch&lt;/code&gt; on a prose container gives you a readable line length regardless of font size. It’s not exactly “60 characters wide” (monospace aside), but it’s close enough and adapts as the font changes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ex&lt;/code&gt; and &lt;code&gt;cap&lt;/code&gt; are handy for optical alignment &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; centering an icon with text, nudging a superscript, that kind of thing:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;/* nudge icon to sit at cap height */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;icon&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#BB9AF7&quot;&gt;cap&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;  width&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#BB9AF7&quot;&gt;cap&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;/* rough measure: ex ≈ 0.5em for most fonts */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#0DB9D7&quot;&gt;sup&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;  vertical-align&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#F7768E&quot;&gt;ex&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>css</category></item><item><title>CSS viewport units: dvh, lvh, svh (and their *vw siblings)</title><link>https://itsmeray.com/til/css-viewport-units/</link><guid isPermaLink="true">https://itsmeray.com/til/css-viewport-units/</guid><pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;vh&lt;/code&gt; and &lt;code&gt;vw&lt;/code&gt; have been around forever, but mobile browsers broke them. The URL bar in Chrome/Safari shrinks when you scroll &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; so &lt;code&gt;100vh&lt;/code&gt; is taller than the visible area when the page loads, causing content to get clipped or produce unexpected scroll.&lt;/p&gt;
&lt;p&gt;The fix landed as three new variants:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Unit&lt;/th&gt;&lt;th&gt;What it tracks&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;svh&lt;/code&gt; / &lt;code&gt;svw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Small&lt;/strong&gt; viewport &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; assumes UI chrome (address bar, nav) is fully visible&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;lvh&lt;/code&gt; / &lt;code&gt;lvw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Large&lt;/strong&gt; viewport &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; assumes UI chrome is hidden/retracted&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;dvh&lt;/code&gt; / &lt;code&gt;dvw&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Dynamic&lt;/strong&gt; viewport &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; updates as UI chrome appears and disappears&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Same split applies to &lt;code&gt;vi&lt;/code&gt; / &lt;code&gt;vb&lt;/code&gt; (inline/block axis) if you need those.&lt;/p&gt;
&lt;h2 id=&quot;which-one-to-use&quot;&gt;Which one to use&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dvh&lt;/code&gt; sounds like the obvious answer but it causes layout reflows as the user scrolls, which can make the page jump. Use it sparingly.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;/* hero that actually fills the screen on load */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;hero&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;  min-height&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#F7768E&quot;&gt;svh&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;/* sticky footer pinned to the retracted-chrome size */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;modal&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;  height&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#F7768E&quot;&gt;dvh&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;svh&lt;/code&gt; is the safe default for full-screen sections &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; it matches what the user sees when they first land on the page. &lt;code&gt;lvh&lt;/code&gt; is mainly useful when you know the chrome is gone (fullscreen video, PWA display mode).&lt;/p&gt;
&lt;div class=&quot;callout callout--info&quot; role=&quot;alert&quot;&gt;&lt;span class=&quot;callout__icon&quot; data-callout-icon=&quot;info&quot;&gt;&lt;/span&gt;&lt;div class=&quot;callout__body&quot;&gt;&lt;div class=&quot;callout__content&quot;&gt;&lt;p&gt;All three have been supported across Chrome, Safari, and Firefox since 2023. The old &lt;code&gt;vh&lt;/code&gt; still works &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; just be aware of what it actually measures (inconsistent across browsers, generally the large viewport).&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><category>css</category></item><item><title>BEM: Blocks, Elements, and Modifiers</title><link>https://itsmeray.com/til/bem-css-methodology/</link><guid isPermaLink="true">https://itsmeray.com/til/bem-css-methodology/</guid><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BEM (Block, Element, Modifier) is a CSS naming convention created by Yandex in 2009. The pattern is &lt;code&gt;.block__element--modifier&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Block&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; a standalone component (&lt;code&gt;.card&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Element&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; a part of that block that has no meaning on its own (&lt;code&gt;.card__title&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modifier&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; a variation or state of a block/element (&lt;code&gt;.card--featured&lt;/code&gt;, &lt;code&gt;.card__title--large&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;card&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;              /* Block */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;card__title&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;       /* Element */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;card--featured&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt;    /* Modifier */&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The double underscore (&lt;code&gt;__&lt;/code&gt;) signals “this belongs to,” and the double hyphen (&lt;code&gt;--&lt;/code&gt;) signals “this is a variant of.” Elements are always flat &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; &lt;code&gt;.card__body__text&lt;/code&gt; is wrong, &lt;code&gt;.card__text&lt;/code&gt; is right.&lt;/p&gt;
&lt;h2 id=&quot;why-bem&quot;&gt;Why BEM?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flat specificity&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; single-class selectors only, no &lt;code&gt;!important&lt;/code&gt; wars&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-documenting&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; &lt;code&gt;.search-form__input--disabled&lt;/code&gt; needs no comment&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encapsulation&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; &lt;code&gt;.card__title&lt;/code&gt; won’t bleed into &lt;code&gt;.header__title&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Grep-friendly&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; search &lt;code&gt;.card&lt;/code&gt; to find everything related to that block&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;dx-tradeoffs&quot;&gt;DX tradeoffs&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; readable diffs, easy onboarding, human-readable DevTools classes (&lt;code&gt;.search-form__input--error&lt;/code&gt; vs &lt;code&gt;.sc-dkPtRN&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt; verbose class names, naming fatigue (“is this a block or an element?”), awkward JS access &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; &lt;code&gt;$style[&apos;card__body--muted&apos;]&lt;/code&gt; because &lt;code&gt;--&lt;/code&gt; breaks dot notation.&lt;/p&gt;
&lt;h2 id=&quot;use-case-in-vue&quot;&gt;Use Case in Vue&lt;/h2&gt;
&lt;p&gt;In Vue SFCs, &lt;strong&gt;you probably don’t need BEM&lt;/strong&gt;. Vue’s &lt;code&gt;&amp;#x3C;style scoped&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;style module&gt;&lt;/code&gt; already handle encapsulation, so BEM’s namespacing becomes redundant. Simpler alternatives:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flat semantic names&lt;/strong&gt; with scoped styles &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; just &lt;code&gt;.title&lt;/code&gt;, &lt;code&gt;.body&lt;/code&gt;, &lt;code&gt;.muted&lt;/code&gt;. The scoping attribute (&lt;code&gt;[data-v-f3f3eg9]&lt;/code&gt;) prevents collisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CSS Modules with camelCase&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; better when you need JS class binding:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;vue&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; :class&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;$style.card&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; :class&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;$style.title&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9AA5CE&quot;&gt;Hello&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; :class&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;[$style.body, $style.bodyMuted]&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9AA5CE&quot;&gt;World&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;template&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; module&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;card&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt; /* ... */&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt; /* ... */&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt; /* ... */&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;bodyMuted&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-light-font-style:italic;--shiki-dark:#51597D;--shiki-dark-font-style:italic&quot;&gt; /* ... */&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clean dot notation, no brackets. Configure with &lt;code&gt;css.modules.localsConvention: &apos;camelCaseOnly&apos;&lt;/code&gt; in Vite.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data attributes for states&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; &lt;code&gt;:data-featured=&quot;featured&quot;&lt;/code&gt; instead of modifier classes. No class binding juggling, and state shows up clearly in DevTools.&lt;/p&gt;
&lt;h2 id=&quot;when-to-use-what&quot;&gt;When to use what&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BEM&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; shared/global CSS, design systems, vanilla CSS projects, large teams&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scoped styles + flat names&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; Vue/Astro components that don’t need JS class binding&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSS Modules + camelCase&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; components that need dynamic class binding in JS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Utility-first (Tailwind)&lt;/strong&gt; &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; when you’d rather skip naming altogether&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>css</category></item><item><title>Enhanced Code Blocks with Shiki Transformers</title><link>https://itsmeray.com/til/enhanced-code-blocks/</link><guid isPermaLink="true">https://itsmeray.com/til/enhanced-code-blocks/</guid><pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shiki supports built-in transformers for filename labels, line highlighting, and diff notation &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; no plugins needed.&lt;/p&gt;
&lt;h2 id=&quot;filename-labels&quot;&gt;Filename Labels&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;title=&quot;filename&quot;&lt;/code&gt; to any fenced code block to show a filename tab:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot; data-title=&quot;src/utils/greet.ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt; greet&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#BB9AF7;--shiki-dark-font-style:italic&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; `&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;Hello, &lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;line-highlighting&quot;&gt;Line Highlighting&lt;/h2&gt;
&lt;p&gt;Highlight specific lines with &lt;code&gt;// [!code highlight]&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night has-highlighted&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot; data-title=&quot;global.css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BB9AF7&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line highlighted&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#C0CAF5&quot;&gt;  --color-primary&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9AA5CE&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9AA5CE&quot;&gt;6366f1&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#C0CAF5&quot;&gt;  --color-secondary&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9AA5CE&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9AA5CE&quot;&gt;a855f7&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line highlighted&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#C0CAF5&quot;&gt;  --radius-md&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 0.5&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#F7768E&quot;&gt;rem&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;focus&quot;&gt;Focus&lt;/h2&gt;
&lt;p&gt;Dim everything except the lines marked with &lt;code&gt;// [!code focus]&lt;/code&gt;. Hovering the block restores full opacity.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night has-focused&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot; data-title=&quot;src/utils/auth.ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#0DB9D7&quot;&gt; hash&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;bcrypt&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#0DB9D7&quot;&gt; db&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;./db&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line focused&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt; createUser&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt; password&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line focused&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#BB9AF7&quot;&gt; hashed&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#BB9AF7;--shiki-dark-font-style:italic&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt; hash&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;password&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line focused&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#BB9AF7;--shiki-dark-font-style:italic&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; db&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#7DCFFF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; email&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt; password&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; hashed&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt; deleteUser&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#BB9AF7;--shiki-dark-font-style:italic&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; db&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#7DCFFF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt;delete&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;diff-view&quot;&gt;Diff View&lt;/h2&gt;
&lt;p&gt;Show additions and removals with &lt;code&gt;// [!code ++]&lt;/code&gt; and &lt;code&gt;// [!code --]&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night has-diff&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot; data-title=&quot;config.ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#BB9AF7&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line diff remove&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt;  theme&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line diff add&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt;  theme&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line diff remove&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt;  prefetch&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line diff add&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt;  prefetch&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#73DACA&quot;&gt;  lang&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;en&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;all-together&quot;&gt;All Together&lt;/h2&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night has-highlighted has-diff&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;tsx&quot; data-title=&quot;src/components/Button.tsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt; ButtonProps&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;  label&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#0DB9D7&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line diff add&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;  variant&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-light-font-style:italic;--shiki-dark:#9ECE6A;--shiki-dark-font-style:inherit&quot;&gt;primary&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-light-font-style:italic;--shiki-dark:#9ECE6A;--shiki-dark-font-style:inherit&quot;&gt;ghost&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line highlighted&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#7DCFFF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#8839EF;--shiki-dark:#BB9AF7&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-light-font-style:italic;--shiki-dark:#7AA2F7;--shiki-dark-font-style:inherit&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#E64553;--shiki-light-font-style:italic;--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit&quot;&gt; variant&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;primary&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt; }&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-light-font-style:italic;--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit&quot;&gt; ButtonProps&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#8839EF;--shiki-light-font-style:inherit;--shiki-dark:#BB9AF7;--shiki-dark-font-style:italic&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line highlighted&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;btn btn--&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;variant&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#C0CAF5&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#7DCFFF&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#F7768E&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BA3C97&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;  )&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>astro</category><category>markdown</category></item><item><title>Irradiation Illusion: Why white looks bigger than black</title><link>https://itsmeray.com/til/irradiation-illusion/</link><guid isPermaLink="true">https://itsmeray.com/til/irradiation-illusion/</guid><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://pbs.twimg.com/media/HB-8IOgaYAAlu_8?format=jpg&amp;#x26;name=large&quot; alt=&quot;Irradiation illusion&quot;&gt;
&lt;/div&gt;
&lt;p&gt;White text on dark backgrounds looks heavier than it actually is. Light bleeds past the edges of each letter, making the font appear bolder than it is.&lt;/p&gt;
&lt;p&gt;Road sign designers figured this out decades ago. Fonts like Wayfinding Sans Pro built for highway signage ship two versions of every weight. The one meant for light-on-dark is thinner, so both look equal to a driver at speed.&lt;/p&gt;
&lt;p&gt;Same idea works on the web. Make your light mode font-weight slightly heavier than your dark mode. Both will feel the same, even though they aren’t.&lt;/p&gt;
&lt;p&gt;The fix is rather simple, just adjust the weights for both modes:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#BB9AF7&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#C0CAF5&quot;&gt;    --font-weight-regular&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 450&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#BB9AF7&quot;&gt;data-theme&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#C0CAF5&quot;&gt;    --font-weight-regular&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; 400&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kind of detail no one notices when it’s right. Everyone feels when it’s wrong.&lt;/p&gt;
&lt;p&gt;Sometimes even after doing all the right things in theory, there are high chances it can feel off. Some manual adjustments has to be done in order for the design to appear natural in practice (not theory).&lt;/p&gt;</content:encoded><category>css</category></item><item><title>CSS Text Box Trim</title><link>https://itsmeray.com/til/css-text-box-trim/</link><guid isPermaLink="true">https://itsmeray.com/til/css-text-box-trim/</guid><pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Text always has weird extra space above capitals and below the baseline.&lt;/p&gt;
&lt;div align=&quot;center&quot;&gt;
  &lt;img src=&quot;https://github.com/jantimon/text-box-trim-examples/raw/main/docs/intro.webp&quot; alt=&quot;Text box trim&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Just a simple two-line fix, perfectly trims the text box to the actual glyph bounds. No more padding hacks.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#E0AF68&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#DF8E1D;--shiki-dark:#9ECE6A&quot;&gt;text-box-trim&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;  text-box-trim&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#FF9E64&quot;&gt; both&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#9ABDF5&quot;&gt;  text-box-edge&lt;/span&gt;&lt;span style=&quot;color:#179299;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#BB9AF7&quot;&gt; cap &lt;/span&gt;&lt;span style=&quot;color:#4C4F69;--shiki-dark:#FF9E64&quot;&gt;alphabetic&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As of early 2025, text-box-trim is available by default in Chrome 133+, Edge 133+, and Safari 18.2+ (&lt;em&gt;sadly no Firefox womp womp&lt;/em&gt;).&lt;/p&gt;</content:encoded><category>css</category></item><item><title>VS Code Smooth Scrolling</title><link>https://itsmeray.com/til/vscode-smooth-scrolling/</link><guid isPermaLink="true">https://itsmeray.com/til/vscode-smooth-scrolling/</guid><pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;pre class=&quot;astro-code astro-code-themes catppuccin-latte tokyo-night&quot; style=&quot;background-color:#eff1f5;--shiki-dark-bg:#1a1b26;color:#4c4f69;--shiki-dark:#a9b1d6; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;editor.cursorBlinking&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;smooth&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;editor.cursorSmoothCaretAnimation&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#9ECE6A&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#40A02B;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;editor.smoothScrolling&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;terminal.integrated.smoothScrolling&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;color:#1E66F5;--shiki-dark:#7AA2F7&quot;&gt;workbench.list.smoothScrolling&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#89DDFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FE640B;--shiki-dark:#FF9E64&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#7C7F93;--shiki-dark:#9ABDF5&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>tooling</category></item><item><title>CSS :has() selector is a parent selector</title><link>https://itsmeray.com/til/css-has-selector/</link><guid isPermaLink="true">https://itsmeray.com/til/css-has-selector/</guid><pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The &lt;code&gt;:has()&lt;/code&gt; pseudo-class lets you style a parent based on its children. For example, &lt;code&gt;div:has(&gt; img)&lt;/code&gt; selects any &lt;code&gt;div&lt;/code&gt; that directly contains an &lt;code&gt;img&lt;/code&gt;. No more JavaScript hacks for parent selection!&lt;/p&gt;</content:encoded><category>css</category></item><item><title>git switch vs git checkout</title><link>https://itsmeray.com/til/git-switch-vs-checkout/</link><guid isPermaLink="true">https://itsmeray.com/til/git-switch-vs-checkout/</guid><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;git switch&lt;/code&gt; was introduced in Git 2.23 as a safer, more focused alternative to &lt;code&gt;git checkout&lt;/code&gt; for switching branches. Use &lt;code&gt;git switch&lt;/code&gt; for branches and &lt;code&gt;git restore&lt;/code&gt; for files &lt;span data-dash=&quot;em&quot; data-id=&quot;prose-dash-em&quot;&gt;—&lt;/span&gt; clearer intent, fewer foot-guns.&lt;/p&gt;</content:encoded><category>git</category><category>tooling</category></item></channel></rss>