<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Jo&#39;s Blog</title>
  <subtitle>My blog about tech and life and stuff.</subtitle>
  <link href="https://j0.lol/feed.xml" rel="self" />
  <link href="https://j0.lol/" />
  <updated>2026-03-12T23:20:00Z</updated>
  <id>https://j0.lol/</id>
  <author>
    <name>Jo Null</name>
  </author>
  <entry>
    <title>Scoping CSS</title>
    <link href="https://j0.lol/blog/scoped-css/" />
    <updated>2026-03-12T23:20:00Z</updated>
    <id>https://j0.lol/blog/scoped-css/</id>
    <content type="html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/baseline-status@1/baseline-status.min.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;p&gt;As web developers, we create websites.
As designers, we want to make components.
Since the platform was not originally designed for this, there can be papercuts.&lt;/p&gt;
&lt;p&gt;Ideally, we would be able to create isolated components,
and have the styles for them not leak out into any other parts of our application.&lt;/p&gt;
&lt;p&gt;I&#39;m going to go through as many ways I can think of at doing this,
to demonstrate how much of a problem this is. I&#39;ll be making a &amp;quot;Card&amp;quot; component.
A card really is just something visually distinct from the other parts of your website, as far as I can tell.&lt;/p&gt;
&lt;p&gt;I&#39;ll go over:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vanilla CSS&lt;/li&gt;
&lt;li&gt;Component Libraries&lt;/li&gt;
&lt;li&gt;Web Components&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Vanilla CSS&lt;/h2&gt;
&lt;p&gt;Stuff you can do that isnt like, a component library like React or Svelte.&lt;/p&gt;
&lt;h3&gt;Don&#39;t scope at all&lt;/h3&gt;
&lt;p&gt;Make a plain css file!&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-tg&gt;body&lt;/a-tg&gt; &lt;a-o&gt;&amp;gt;&lt;/a-o&gt; &lt;a-p&gt;:&lt;/a-p&gt;&lt;a-at&gt;is&lt;/a-at&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-tg&gt;header&lt;/a-tg&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-tg&gt;footer&lt;/a-tg&gt;&lt;a-p&gt;)&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;background-color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightGrey&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;background-color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightSlateGrey&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have everything in one CSS file, this can get a bit unwieldy...
&lt;ul&gt;
&lt;li&gt;We might want to structure our CSS to prevent this&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We might introduce a global rule (e.g. all &lt;code&gt;h2&lt;/code&gt;s are green) that might not want to be in certain areas of the website
&lt;ul&gt;
&lt;li&gt;This is why we might want to scope our rules&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Not really&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;BEM&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://getbem.com/&quot;&gt;BEM&lt;/a&gt; is a naming convention for components that tries to solve the scoping issue.&lt;/p&gt;
&lt;p&gt;In BEM, you name your classes like this: &lt;code&gt;.Block__element--modifier&lt;/code&gt;. (There seems to be debate on whether to use &lt;code&gt;__&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt; to designate elements. &lt;a href=&quot;https://getbem.com&quot;&gt;GetBEM&lt;/a&gt; and &lt;a href=&quot;https://bem.info&quot;&gt;Bem.info&lt;/a&gt; disagree on this, and there appears to be no authoritative sauce …)&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt; &lt;a-at&gt;class&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;Card&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-at&gt;class&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;Card__header&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is the card&amp;#39;s Header&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;scss&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;Card&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-s&gt;LightSlateGrey&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

    &lt;a-tg&gt;&amp;amp;&lt;/a-tg&gt;&lt;a-pr&gt;__header&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-s&gt;Green&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;This is nice, and the syntax is quite easy to work with, so it feels like it would be a good solution.
Sadly, you do need a CSS preprocessor, Sass, for the above functionality.
In vanilla CSS, you&#39;d have to spell out &lt;code&gt;.Card__header&lt;/code&gt; and et cetera. Annoying on a large codebase, I would assume. (I suspect that standards/vendor people liked Sass&#39;s &lt;code&gt;&amp;amp;&lt;/code&gt; syntax, but &amp;quot;string concatenation on arbitrary selectors&amp;quot; was not a very attractive thing to add to CSS, so they compromised on a still-pretty-good solution.)&lt;/p&gt;
&lt;p&gt;The other issue is that this requires some level of discipline to keep up. It&#39;s a constant effort to make sure that you make everything a class, name it according to BEM, just to solve scoping issues.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Quite well (I would put each &amp;quot;block&amp;quot; in a separate file)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Good, if you can keep it up&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Nesting&lt;/h3&gt;
&lt;p&gt;&lt;baseline-status featureId=&quot;nesting&quot;&gt;&lt;/baseline-status&gt;&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; Green&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;

    &lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightSlateGrey&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;Nesting is cool! It means you can scope your CSS to the parent container without chaining selectors like &lt;code&gt;.card h2&lt;/code&gt;. You can also set a lower bound, which is something that BEM can do&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Maybe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;It&#39;s okay, specificity can get high if you nest a lot.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;@Scope&lt;/h3&gt;
&lt;p&gt;&lt;baseline-status featureId=&quot;scope&quot;&gt;&lt;/baseline-status&gt;&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;@scope &lt;a-p&gt;(.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt;&lt;a-p&gt;)&lt;/a-p&gt; &lt;a-c&gt;/* to (.lowerBound, .otherBound) */&lt;/a-c&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-p&gt;:&lt;/a-p&gt;&lt;a-at&gt;scope&lt;/a-at&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-c&gt;/* targets .card */&lt;/a-c&gt;
   	    &lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightSlateGrey&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;

    &lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; Green&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;Very similar to nesting. Unlike nesting though,
it handles specificity very differently.
While you can think of nesting as &amp;quot;resolving&amp;quot; to a non-nested CSS,
&lt;code&gt;@scope&lt;/code&gt; can&#39;t be resolved in this way.&lt;/p&gt;
&lt;p&gt;Scope does priority in rule matching very differently, based on proximity rules.
MDN has a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@scope#specificity_in_scope&quot;&gt;good explainer&lt;/a&gt; on this.
I&#39;d love to go more in depth on priority in a later blog post, as it seems like a quite advanced topic in CSS.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;It&#39;s better than nesting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Very well&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;In Component Libraries&lt;/h2&gt;
&lt;p&gt;Focusing on React (but probably applicable elsewhere):&lt;/p&gt;
&lt;h3&gt;CSS Modules&lt;/h3&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;Card.module.css&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightSlateGrey&lt;a-p&gt;;&lt;/a-p&gt;

    &lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; Green&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;jsx&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;Card.jsx (You&#39;re writing React now, sorry)&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;import&lt;/a-k&gt; &lt;a-v&gt;styles&lt;/a-v&gt; &lt;a-k&gt;from&lt;/a-k&gt; &lt;a-s&gt;&amp;quot;./Card.module.css&amp;quot;&lt;/a-s&gt;
&lt;a-k&gt;export&lt;/a-k&gt; &lt;a-k&gt;default&lt;/a-k&gt; &lt;a-k&gt;function&lt;/a-k&gt; &lt;a-cr&gt;Card&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;return&lt;/a-pr&gt; &lt;a-p&gt;(&lt;/a-p&gt;
        &lt;a-o&gt;&amp;lt;&lt;/a-o&gt;&lt;a-v&gt;div&lt;/a-v&gt; &lt;a-pr&gt;className&lt;/a-pr&gt;&lt;a-o&gt;=&lt;/a-o&gt;&lt;a-p&gt;{&lt;/a-p&gt;&lt;a-v&gt;styles&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt;&lt;a-p&gt;}&lt;/a-p&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
            &lt;a-o&gt;&amp;lt;&lt;/a-o&gt;&lt;a-v&gt;h2&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;This is the card&amp;#39;s Header&amp;lt;/&lt;a-v&gt;h2&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
        &amp;lt;/&lt;a-v&gt;div&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
    &lt;a-p&gt;)&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;CSS Modules only scope &lt;em&gt;classes&lt;/em&gt;. In fact, they rewrite your classes for you. (Note that you can sidestep this magic through &lt;code&gt;:global(.selector)&lt;/code&gt; if needed.) This would be a &amp;quot;foot gun&amp;quot;:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;Card.module.css&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;card&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-c&gt;/* ... */&lt;/a-c&gt; &lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; Green&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;So you&#39;re not really writing scoped CSS. You&#39;re writing normal CSS where the classes &lt;em&gt;happen&lt;/em&gt; to be scoped. At least it means you can reuse class names, I guess!&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Decent, though you do need to make sure to make sure everything is focused on classes…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Autogenerating unique class names works well for scoping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;CSS-in-JS&lt;/h3&gt;
&lt;p&gt;I don&#39;t really have enough experience with these libraries to touch on them, but &lt;a href=&quot;https://css-tricks.com/a-thorough-analysis-of-css-in-js/&quot;&gt;CSS-Tricks has a good analysis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;They all have some funky way of inputting CSS though, like a Lit-style tagged template or an object.&lt;/p&gt;
&lt;h3&gt;Tailwind&lt;/h3&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt; &lt;a-at&gt;class&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;bg-[LightSlateGrey]&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-at&gt;class&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;text-[Green]&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is the card&amp;#39;s Header&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;We are no longer writing CSS.&lt;/p&gt;
&lt;p&gt;I&#39;m lumping in Tailwind with React because it really shines when you are using a real component library.
Since you&#39;re already naming your components in the class and file name it does feel quite redundant to repeat naming things.
&lt;s&gt;It also feels horribly lazy, like React…&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;Not writing CSS really does help with scoping though.
Since you&#39;re declaring every element with classes that point to it directly,
you aren&#39;t going to have any issues with it targeting other elements.
This is a core design idea of Tailwind (and where I philosophically oppose it!)&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Worse than any CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Very&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Style property&lt;/h3&gt;
&lt;p&gt;It&#39;s a property, not an attribute.&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;jsx&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-o&gt;&amp;lt;&lt;/a-o&gt;&lt;a-v&gt;div&lt;/a-v&gt; &lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-o&gt;=&lt;/a-o&gt;&lt;a-p&gt;{{&lt;/a-p&gt; &lt;a-pr&gt;background&lt;/a-pr&gt;: &lt;a-s&gt;&amp;quot;LightSlateGrey&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;}}&lt;/a-p&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
    &lt;a-o&gt;&amp;lt;&lt;/a-o&gt;&lt;a-v&gt;h2&lt;/a-v&gt; &lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-o&gt;=&lt;/a-o&gt;&lt;a-p&gt;{{&lt;/a-p&gt; &lt;a-pr&gt;color&lt;/a-pr&gt;: &lt;a-s&gt;&amp;quot;Green&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;}}&lt;/a-p&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;This is the card&amp;#39;s Header&amp;lt;/&lt;a-v&gt;h2&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
&amp;lt;/&lt;a-v&gt;div&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;weh.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;:(&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Very&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Web components&lt;/h2&gt;
&lt;p&gt;Finally.
I&#39;ve &lt;a href=&quot;https://j0.lol/blog/webcomponentsforfunandprofit/&quot;&gt;talked about this before&lt;/a&gt;, but the Shadow DOM means Web Components are by definition scoped separately from the rest of the document. This means you can go wild and put anything in the style tags, and it will only be scoped to your component.&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;js&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;card.js&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-v&gt;customElements&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;define&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;#39;my-card&amp;#39;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt;
  &lt;a-k&gt;class&lt;/a-k&gt; &lt;a-k&gt;extends&lt;/a-k&gt; &lt;a-cr&gt;HTMLElement&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-f&gt;constructor&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
      &lt;a-v&gt;super&lt;/a-v&gt;&lt;a-p&gt;();&lt;/a-p&gt;

      &lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;template&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;getElementById&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;#39;my-card-template&amp;#39;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
      &lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;templateContent&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;template&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;content&lt;/a-pr&gt;&lt;a-p&gt;;&lt;/a-p&gt;

      &lt;a-v&gt;this&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;attachShadow&lt;/a-f&gt;&lt;a-p&gt;({&lt;/a-p&gt; &lt;a-pr&gt;mode&lt;/a-pr&gt;: &lt;a-s&gt;&amp;#39;open&amp;#39;&lt;/a-s&gt; &lt;a-p&gt;}).&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;
        &lt;a-v&gt;templateContent&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;cloneNode&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-co&gt;true&lt;/a-co&gt;&lt;a-p&gt;)&lt;/a-p&gt;
      &lt;a-p&gt;);&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
  &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;);&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt; &lt;a-at&gt;id&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;my-card-template&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;style&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
     &lt;a-p&gt;:&lt;/a-p&gt;&lt;a-at&gt;host&lt;/a-at&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-c&gt;/* targets the element wrapping over shadow root */&lt;/a-c&gt;
       &lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; LightSlateGrey&lt;a-p&gt;;&lt;/a-p&gt;
     &lt;a-p&gt;}&lt;/a-p&gt;
     &lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
         &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; Green&lt;a-p&gt;;&lt;/a-p&gt;
     &lt;a-p&gt;}&lt;/a-p&gt;
   &lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;style&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;

   &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is the card&amp;#39;s Header&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
   &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is the card&amp;#39;s content&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
  &lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;

  &lt;a-c&gt;&amp;lt;!-- later... --&amp;gt;&lt;/a-c&gt;

  &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;my-card&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;foo&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;my-card&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Met?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Good Structure&lt;/td&gt;
&lt;td&gt;Meh. Web components are verbose.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoping&lt;/td&gt;
&lt;td&gt;Quite well? Unsure how &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; plays into this, but that&#39;s kind of a donut scope like you can do with &lt;code&gt;@scope&lt;/code&gt;…&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;/p&gt;&lt;aside class=&quot;speech-box&quot;&gt;
&lt;div class=&quot;speech-character character-undefined&quot;&gt;
&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p class=&quot;speech-content&quot;&gt;
I probably missed something! I hope this was an okay overview though. Personally, I&#39;d like to see a website that uses all of these. I bet that would be hard to maintain. &lt;small&gt;Oh! And it would be cool to see some sort of component library like Svelte/Vue try &lt;code&gt;@scope&lt;/code&gt; instead of doing auto-generated class name prefixing.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
		&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>Editor Configs and stuff</title>
    <link href="https://j0.lol/blog/editor-configurations/" />
    <updated>2026-03-10T00:00:00Z</updated>
    <id>https://j0.lol/blog/editor-configurations/</id>
    <content type="html">&lt;p&gt;I&#39;ve been switching between editors a lot recently. I&#39;m a macOS user (currently…) so that does dictate some of my editor choices.&lt;/p&gt;
&lt;p&gt;It also depends on my mood, I guess. Sometimes I prefer to use &lt;a href=&quot;https://helix-editor.com/&quot;&gt;Helix&lt;/a&gt; in
the terminal, and sometimes I prefer to use &lt;a href=&quot;https://nova.app&quot;&gt;Nova&lt;/a&gt; because it feels very clean to use. Occasionally, I will use Zed or VS Code for features.&lt;/p&gt;
&lt;h2&gt;Helix configuration&lt;/h2&gt;
&lt;p&gt;Helix is very &amp;quot;batteries included&amp;quot;, in that it comes with a lot of what you
would expect by default. This is nice because configuration is annoying.
I used to be an avid NeoVim user, and the configuration can get quite… intense?
You basically need a package manager for it.&lt;/p&gt;
&lt;p&gt;Helix comes with LSP support (program that performs IDE functions for you, specialized for a certain language) and highlighting through Tree-Sitter.
Unlike other LSP IDEs, it does not bundle them and expects you to install
the IDEs through your package manager.&lt;/p&gt;
&lt;p&gt;You can view LSP support through the &lt;code&gt;hx --health&lt;/code&gt; argument (command?) which will spit out something like this:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			
			&lt;pre&gt;&lt;code&gt;$ hx --health rust
Configured language servers:
  ✓ rust-analyzer: /Users/jo/.cargo/bin/rust-analyzer
Configured debug adapter:
  ✘ &amp;apos;lldb-dap&amp;apos; not found in $PATH
Configured formatter: None
Tree-sitter parser: ✓
Highlight queries: ✓
Textobject queries: ✓
Indent queries: ✓&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;My helix configuration is quite short:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;toml&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;~/.config/helix/config.toml&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-pr&gt;theme&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;rose_pine_dawn&amp;quot;&lt;/a-s&gt;

&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;editor&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;auto-format&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-co&gt;true&lt;/a-co&gt;

&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;editor&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;cursor-shape&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;insert&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;bar&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;normal&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;block&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;select&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;underline&amp;quot;&lt;/a-s&gt;

&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;keys&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;normal&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;x&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;select_line_below&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;X&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;select_line_above&amp;quot;&lt;/a-s&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;… This is a bit misleading, though.
First off, there&#39;s &lt;code&gt;languages.toml&lt;/code&gt;,
which I use for configuring language specific stuffs:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;toml&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;&amp;hellip;/languages.toml&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;language-server&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;rust-analyzer&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;config&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;check&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;clippy&amp;quot;&lt;/a-s&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;To explain the rest of my config I&#39;ll have to go through the tools I&#39;m using.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kristoff-it/superhtml&quot;&gt;SuperHTML&lt;/a&gt; is an LSP (and some other stuff) for HTML. It&#39;s lints (warnings? errors?) are based on spec, instead of Prettier which seems to be &amp;quot;vibes&amp;quot;. Specifically, it doesn&#39;t have &amp;quot;end slashes&amp;quot; like &lt;span class=&quot;nowrap&quot;&gt;&lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt;&lt;/span&gt;. I hate end slashes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dprint.dev/&quot;&gt;DPrint&lt;/a&gt; is a multi-language code formatter, which seems to be maintained by a member of the Deno team.&lt;/li&gt;
&lt;li&gt;VSCode has a few built in language servers, which have been put on brew and such.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://emmet.io/&quot;&gt;Emmet&lt;/a&gt; is basically a very easy way to do very succinct macros for HTML and CSS. I guess people don&#39;t do this anymore because of ai... whatever..&lt;/li&gt;
&lt;/ul&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;toml&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;&amp;hellip;/languages.toml (continued)&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;language-server&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;emmet-lsp&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;emmet-language-server&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;args&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;--stdio&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt;

&lt;a-p&gt;[[&lt;/a-p&gt;&lt;a-pr&gt;language&lt;/a-pr&gt;&lt;a-p&gt;]]&lt;/a-p&gt;
&lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;html&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;language-servers&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[{&lt;/a-p&gt; &lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;superhtml&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;except-features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;format&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;]},&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;emmet-lsp&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;auto-format&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-co&gt;true&lt;/a-co&gt;
&lt;a-pr&gt;formatter&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;dprint&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;args&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fmt&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;--stdin&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;html&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-p&gt;[[&lt;/a-p&gt;&lt;a-pr&gt;language&lt;/a-pr&gt;&lt;a-p&gt;]]&lt;/a-p&gt;
&lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;css&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;language-servers&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;vscode-css-language-server&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;except-features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;format&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;]}&lt;/a-p&gt; &lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;auto-format&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-co&gt;true&lt;/a-co&gt;
&lt;a-pr&gt;formatter&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;dprint&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;args&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fmt&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;--stdin&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;css&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-p&gt;[[&lt;/a-p&gt;&lt;a-pr&gt;language&lt;/a-pr&gt;&lt;a-p&gt;]]&lt;/a-p&gt;
&lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;javascript&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;language-servers&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;typescript-language-server&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;except-features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;format&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}]&lt;/a-p&gt;
&lt;a-pr&gt;auto-format&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-co&gt;true&lt;/a-co&gt;
&lt;a-pr&gt;formatter&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;dprint&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;args&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fmt&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;--stdin&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;javascript&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-p&gt;[[&lt;/a-p&gt;&lt;a-pr&gt;language&lt;/a-pr&gt;&lt;a-p&gt;]]&lt;/a-p&gt;
&lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;typescript&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;language-servers&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;name&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;typescript-language-server&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;except-features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;format&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}]&lt;/a-p&gt;
&lt;a-pr&gt;auto-format&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-co&gt;true&lt;/a-co&gt;
&lt;a-pr&gt;formatter&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;command&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;dprint&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;args&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fmt&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;--stdin&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;typescript&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;h3&gt;The context within which I use Helix&lt;/h3&gt;
&lt;p&gt;I usually run Helix inside &lt;a href=&quot;https://zellij.dev&quot;&gt;Zellij&lt;/a&gt;, which is like a less confusing Tmux.
I don&#39;t have too much to say about it, but it allows me to do &lt;a href=&quot;https://docs.jj-vcs.dev/latest/&quot;&gt;JJ&lt;/a&gt; work, compilers, etc. side by side with the editor.&lt;/p&gt;
&lt;h3&gt;Dark/Light mode switching&lt;/h3&gt;
&lt;p&gt;I use macOS, which has a global light/dark theme toggle. I&#39;m personally more of a light mode user nowadays (I have astigmatism, and I&#39;m trying to be more diurnal nowadays) but I use dark mode sometimes so I would like my applications to switch themes.&lt;/p&gt;
&lt;p&gt;Current theme of choice for me is &lt;a href=&quot;https://rosepinetheme.com/&quot;&gt;Rosé Pine&lt;/a&gt;. I used to be a &lt;a href=&quot;https://catppuccin.com/&quot;&gt;Catppuccin&lt;/a&gt; user, but their light theme is just bad. It&#39;s horrible to look at.&lt;/p&gt;
&lt;p&gt;For Helix, this &lt;a href=&quot;https://github.com/helix-editor/helix/issues/2158&quot;&gt;issue&lt;/a&gt; was marked as a wont-fix, so I decided to roll my own script for it. The idea being that if you alias &lt;code&gt;hx&lt;/code&gt; to this function, every time you open it the theme will be synced for you.&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;fish&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;config.fish&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;function&lt;/a-k&gt; &lt;a-f&gt;lightDark&lt;/a-f&gt; -a light dark
    &lt;a-k&gt;if&lt;/a-k&gt; &lt;a-f&gt;defaults&lt;/a-f&gt; read -g AppleInterfaceStyle &lt;a-o&gt;2&amp;gt;&lt;/a-o&gt;/dev/null &lt;a-o&gt;|&lt;/a-o&gt; &lt;a-f&gt;grep&lt;/a-f&gt; -q Dark
        &lt;a-f&gt;echo&lt;/a-f&gt; &lt;a-co&gt;$dark&lt;/a-co&gt;
    &lt;a-k&gt;else&lt;/a-k&gt;
        &lt;a-f&gt;echo&lt;/a-f&gt; &lt;a-co&gt;$light&lt;/a-co&gt;
    &lt;a-k&gt;end&lt;/a-k&gt;
&lt;a-k&gt;end&lt;/a-k&gt;

&lt;a-k&gt;function&lt;/a-k&gt; &lt;a-f&gt;hx&lt;/a-f&gt;
    &lt;a-f&gt;set&lt;/a-f&gt; CONFIG_FILE &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$HOME&lt;/a-co&gt;&lt;a-s&gt;/.config/helix/config.toml&amp;quot;&lt;/a-s&gt;
    &lt;a-f&gt;set&lt;/a-f&gt; THEME &lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;lightDark&lt;/a-f&gt; rose_pine_dawn rose_pine&lt;a-p&gt;)&lt;/a-p&gt;

    &lt;a-k&gt;if&lt;/a-k&gt; &lt;a-f&gt;test&lt;/a-f&gt; &lt;a-o&gt;-f&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;; &lt;a-k&gt;and&lt;/a-k&gt; &lt;a-f&gt;grep&lt;/a-f&gt; -q &lt;a-s&gt;&amp;quot;^theme&amp;quot;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;
        &lt;a-f&gt;sed&lt;/a-f&gt; -i &lt;a-s&gt;&amp;#39;&amp;#39;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;s/^theme = .*/theme = &#92;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$THEME&lt;/a-co&gt;&lt;a-s&gt;&#92;&amp;quot;&lt;/a-s&gt;&lt;a-s&gt;/&amp;quot;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;
    &lt;a-k&gt;end&lt;/a-k&gt;
    &lt;a-f&gt;command&lt;/a-f&gt; hx &lt;a-co&gt;$argv&lt;/a-co&gt;
&lt;a-k&gt;end&lt;/a-k&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;Now, you can basically do the same thing for Zellij…&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;fish&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;config.fish (continued)&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;function&lt;/a-k&gt; &lt;a-f&gt;zellij&lt;/a-f&gt;
    &lt;a-f&gt;set&lt;/a-f&gt; CONFIG_FILE &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$HOME&lt;/a-co&gt;&lt;a-s&gt;/.config/zellij/config.kdl&amp;quot;&lt;/a-s&gt;
    &lt;a-f&gt;set&lt;/a-f&gt; THEME &lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;lightDark&lt;/a-f&gt; rose-pine-dawn rose-pine&lt;a-p&gt;)&lt;/a-p&gt;

    &lt;a-k&gt;if&lt;/a-k&gt; &lt;a-f&gt;test&lt;/a-f&gt; &lt;a-o&gt;-f&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;; &lt;a-k&gt;and&lt;/a-k&gt; &lt;a-f&gt;grep&lt;/a-f&gt; -q &lt;a-s&gt;&amp;quot;^theme&amp;quot;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;
        &lt;a-f&gt;sed&lt;/a-f&gt; -i &lt;a-s&gt;&amp;#39;&amp;#39;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;s/^theme .*/theme &#92;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$THEME&lt;/a-co&gt;&lt;a-s&gt;&#92;&amp;quot;&lt;/a-s&gt;&lt;a-s&gt;/&amp;quot;&lt;/a-s&gt; &lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-co&gt;$CONFIG_FILE&lt;/a-co&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;
    &lt;a-k&gt;end&lt;/a-k&gt;
    &lt;a-f&gt;command&lt;/a-f&gt; zellij &lt;a-co&gt;$argv&lt;/a-co&gt;
&lt;a-k&gt;end&lt;/a-k&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;It&#39;s pretty similar to do the same in bash, zsh, etc. You just want to set an alias instead of a function, probably?&lt;/p&gt;
&lt;h2&gt;Nova&lt;/h2&gt;
&lt;p&gt;It&#39;s a macOS only application. It&#39;s big sells are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Very nice UI&lt;/li&gt;
&lt;li&gt;No AI&lt;/li&gt;
&lt;li&gt;Works well enough&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m still trying to configure it. For me, I&#39;m not totally happy with the linting and formatting for web stuff yet, unlike Helix.&lt;/p&gt;
&lt;p&gt;Extensions I like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;nova://extension/?id=xyz.valknight.rust&amp;amp;name=Rust%203%20(??)&quot;&gt;Rust 3 (??)&lt;/a&gt;: the most up to date Rust plugin, maintained by a friend of mine :)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;nova://extension/?id=panic.HTMLValidator&amp;amp;name=HTML%20Validator&quot;&gt;HTML&lt;/a&gt; and &lt;a href=&quot;nova://extension/?id=panic.CSSValidator&amp;amp;name=CSS%20Validator&quot;&gt;CSS&lt;/a&gt; Validators&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;nova://extension/?id=co.gwil.deno&amp;amp;name=Deno&quot;&gt;Deno&lt;/a&gt; seems to look good, though I&#39;m using &lt;a href=&quot;nova://extension/?id=com.johnlindop.nova-typescript&amp;amp;name=TypeScript%20LSP&quot;&gt;TypeScript LSP&lt;/a&gt; currently.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m missing formatting for HTML/CSS and some linting. There are options, I just haven&#39;t had the time to try all of them yet.&lt;/p&gt;
&lt;p&gt;Nova seems to have a bit of an &amp;quot;issue&amp;quot; about having a bunch of similar extensions.
I think part of the reason is that because Nova is paid, there is not as much of a userbase as something like VS Code.
The only real way to figure out which one you should use is guessing from the last-updated time, though the Rust extensions have helpfully sequentially numbered themselves.&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/editor-configurations/E5YSytX7qH-1158.webp 1158w&quot;&gt;&lt;img width=&quot;1158&quot; height=&quot;706&quot; src=&quot;https://j0.lol/blog/editor-configurations/E5YSytX7qH-1158.jpeg&quot; alt=&quot;Collage of Nova extensions.&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Another thing is that it doesn&#39;t have Helix keybindings. I have come to accept the fact that I will not be able to conform everything to the muscle memory of one text editor. It has a Vim mode, but switching between Helix and Vim is actively harmful to muscle memory. When I&#39;m using Nova, I&#39;m okay with being a bit slower. For most developers, words per minute is not a useful metric for programming work.&lt;/p&gt;
&lt;h2&gt;Zed&lt;/h2&gt;
&lt;p&gt;I love-hate Zed. Compared to Nova it&#39;s ugly and has bad text rendering. However it does have more support for extensions and debugging and stuff, like VS Code.&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;jsonc&lt;/span&gt;
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;~/.config/zed/config.json&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;{
  &lt;a-s&gt;&amp;quot;base_keymap&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;JetBrains&amp;quot;&lt;/a-s&gt;,
  &lt;a-s&gt;&amp;quot;helix_mode&amp;quot;&lt;/a-s&gt;: &lt;a-co&gt;true&lt;/a-co&gt;,
  &lt;a-s&gt;&amp;quot;which_key&amp;quot;&lt;/a-s&gt;: { &lt;a-c&gt;// Gives hints about Helix binds&lt;/a-c&gt;
    &lt;a-s&gt;&amp;quot;enabled&amp;quot;&lt;/a-s&gt;: &lt;a-co&gt;true&lt;/a-co&gt;,
    &lt;a-s&gt;&amp;quot;delay_ms&amp;quot;&lt;/a-s&gt;: &lt;a-n&gt;100&lt;/a-n&gt;,
  },

  &lt;a-s&gt;&amp;quot;tab_size&amp;quot;&lt;/a-s&gt;: &lt;a-n&gt;4&lt;/a-n&gt;,
  &lt;a-s&gt;&amp;quot;project_panel&amp;quot;&lt;/a-s&gt;: {
    &lt;a-s&gt;&amp;quot;sort_mode&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;mixed&amp;quot;&lt;/a-s&gt;,
  },

  &lt;a-s&gt;&amp;quot;format_on_save&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;on&amp;quot;&lt;/a-s&gt;, &lt;a-c&gt;// Yea, it&amp;#39;s a string &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot;??&lt;/a-c&gt;
  &lt;a-s&gt;&amp;quot;autosave&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;on_focus_change&amp;quot;&lt;/a-s&gt;,

  &lt;a-s&gt;&amp;quot;disable_ai&amp;quot;&lt;/a-s&gt;: &lt;a-co&gt;true&lt;/a-co&gt;, &lt;a-c&gt;// thank you zed team&lt;/a-c&gt;

  &lt;a-c&gt;// Inline errors (vsc code lens, default in jetbrains)&lt;/a-c&gt;
  &lt;a-s&gt;&amp;quot;diagnostics&amp;quot;&lt;/a-s&gt;: {
    &lt;a-s&gt;&amp;quot;inline&amp;quot;&lt;/a-s&gt;: {
      &lt;a-s&gt;&amp;quot;enabled&amp;quot;&lt;/a-s&gt;: &lt;a-co&gt;true&lt;/a-co&gt;,
      &lt;a-s&gt;&amp;quot;max_severity&amp;quot;&lt;/a-s&gt;: &lt;a-co&gt;null&lt;/a-co&gt;,
    },
  },

  &lt;a-c&gt;// Theming&lt;/a-c&gt;
  &lt;a-s&gt;&amp;quot;icon_theme&amp;quot;&lt;/a-s&gt;: {
    &lt;a-s&gt;&amp;quot;mode&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;system&amp;quot;&lt;/a-s&gt;,
	  &lt;a-c&gt;// These are from an extension&lt;/a-c&gt;
    &lt;a-s&gt;&amp;quot;light&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;JetBrains New UI Icons (Light)&amp;quot;&lt;/a-s&gt;,
    &lt;a-s&gt;&amp;quot;dark&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;JetBrains New UI Icons (Dark)&amp;quot;&lt;/a-s&gt;,
  },
  &lt;a-s&gt;&amp;quot;ui_font_size&amp;quot;&lt;/a-s&gt;: &lt;a-n&gt;16&lt;/a-n&gt;,
  &lt;a-s&gt;&amp;quot;ui_font_family&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;.SystemUIFont&amp;quot;&lt;/a-s&gt;, &lt;a-c&gt;// SF Pro on macOS&lt;/a-c&gt;
  &lt;a-s&gt;&amp;quot;buffer_font_size&amp;quot;&lt;/a-s&gt;: &lt;a-n&gt;12.0&lt;/a-n&gt;,
  &lt;a-s&gt;&amp;quot;buffer_font_family&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;Maple Mono&amp;quot;&lt;/a-s&gt;,
  &lt;a-s&gt;&amp;quot;buffer_line_height&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;comfortable&amp;quot;&lt;/a-s&gt;,
  &lt;a-s&gt;&amp;quot;theme&amp;quot;&lt;/a-s&gt;: {
    &lt;a-s&gt;&amp;quot;light&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;Rosé Pine Dawn&amp;quot;&lt;/a-s&gt;,
    &lt;a-s&gt;&amp;quot;dark&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;Rosé Pine Moon&amp;quot;&lt;/a-s&gt;,
  },

  &lt;a-c&gt;// Language stuff&lt;/a-c&gt;
  &lt;a-s&gt;&amp;quot;lsp&amp;quot;&lt;/a-s&gt;: {
    &lt;a-s&gt;&amp;quot;rust-analyzer&amp;quot;&lt;/a-s&gt;: {
      &lt;a-s&gt;&amp;quot;initialization_options&amp;quot;&lt;/a-s&gt;: {
        &lt;a-s&gt;&amp;quot;check&amp;quot;&lt;/a-s&gt;: {
          &lt;a-s&gt;&amp;quot;command&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;clippy&amp;quot;&lt;/a-s&gt;
        }
      }
    },
    &lt;a-c&gt;// I&amp;#39;ve omitted some stuff because it&amp;#39;s not too useful&lt;/a-c&gt;
  },
}
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;&lt;p&gt;There&#39;s a bunch more stuff you can configure in Zed (&lt;code&gt;keymap.json&lt;/code&gt;, &lt;code&gt;tasks.json&lt;/code&gt;, project specific overrides, …) but it&#39;s not very useful to me to go that deep. Zed feels like the Vim of GUI editors, in a bad way. I would list my extensions, but you can probably figure out what I would install based on what I&#39;ve listed above.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>New 88×31 Badge</title>
    <link href="https://j0.lol/blog/new-badge/" />
    <updated>2026-03-03T00:00:00Z</updated>
    <id>https://j0.lol/blog/new-badge/</id>
    <content type="html">&lt;p&gt;Hi! Check out the new badge: &lt;img style=&quot;display: inline&quot; src=&quot;https://j0.lol/8831.svg&quot; alt=&quot;Badge for the website J0.lol&quot; width=&quot;88&quot; height=&quot;31&quot;&gt; &lt;/p&gt;

&lt;p&gt;It&#39;s an SVG!&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;img&lt;/a-tg&gt;
  &lt;a-at&gt;src&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;/badge.svg&lt;/a-s&gt;&amp;quot;
  &lt;a-at&gt;alt&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;Badge for the website J0.lol&lt;/a-s&gt;&amp;quot;
  &lt;a-at&gt;width&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;88&lt;/a-s&gt;&amp;quot;
  &lt;a-at&gt;height&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;31&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;If you want to link to it on your website, please don&#39;t &lt;a href=&quot;https://en.wikipedia.org/wiki/Inline_linking&quot;&gt;hotlink&lt;/a&gt; it, copy it instead. (Thanks for linking to my website, if you do!) Not only does it mean that it will make your website take longer to load, but my image links may not stay at the same location forever. Just right click the image and press save!&lt;/p&gt;

&lt;p&gt;I think serving an 88&amp;times;31 badge as an SVG is kind of funny. It&#39;s not &lt;em&gt;really&lt;/em&gt; that size, it can be zoomed in forever! It&#39;s basically at a ratio of 88:31.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Understanding WebGPU</title>
    <link href="https://j0.lol/blog/understanding-webgpu/" />
    <updated>2025-12-19T00:00:00Z</updated>
    <id>https://j0.lol/blog/understanding-webgpu/</id>
    <content type="html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/baseline-status@1/baseline-status.min.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;baseline-status featureid=&quot;webgpu&quot;&gt;&lt;/baseline-status&gt;

&lt;p&gt;
WebGPU is an API for interacting with the GPU, from the web browser. WebGPU does not exist as a program, but it is a standardized interface between the web browser and the GPU. Browsers have to implement WebGPU. In fact, web browsers can be largely thought of as bundles of implementations of web standards, for example HTML.
&lt;/p&gt;

&lt;p&gt;As software developers, we have a lot of ways to talk to the GPU. Most of the time, however, this is strictly dependant on what platform we are writing code for. Here is a simplified flowchart:&lt;/p&gt;

&lt;figure style=&quot;background-color: rgb(249 250 251)&quot;&gt;
&lt;img alt=&quot;A flowchart. It shows what graphics API you would use to talk to the GPU, depending on what device and operating system you are on. On the web, you can use WebGL and WebGPU. On Windows you can use DirectX 12, Vulkan, and OpenGL. On Linux, you can use Vulkan and OpenGL. On MacOS, you can use Metal and OpenGL, though OpenGL is deprecated. On Android, you can use Vulkan and OpenGL ES. On iOS, you can use Metal and OpenGL, though OpenGL is deprecated. &quot; src=&quot;https://vps.j0.lol/website-assets/shapes%20at%2025-12-19%2018.34.58.svg&quot;&gt;
&lt;figcaption&gt;Chekov&#39;s &lt;em&gt;(deprecated)&lt;/em&gt; tag.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let&#39;s imagine for a second that you are a browser vendor. You want to target &lt;em&gt;all&lt;/em&gt; of these platforms, so you have to make and implement an API that turns the browser&#39;s GPU commands into the OS&#39;s GPU commands. You may notice that all of them (mostly) have OpenGL support. So you make WebGL as a wrapper over OpenGL.&lt;/p&gt;

&lt;h2&gt;A short history lesson&lt;/h2&gt;

&lt;figure style=&quot;background-color: rgb(249 250 251)&quot;&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/understanding-webgpu/ZFi2Fx8vva-2064.webp 2064w&quot;&gt;&lt;img alt=&quot;A timeline of Graphics API development. It goes from 1992 to 2021. 1992: OpenGL. 1996: Direct3D. 2011: WebGL. 2014: Metal. 2015: DirectX 12. 2016: Vulkan. 2021: WebGPU. Metal, Vulkan, and DirectX 12 are grouped together as &#39;New Era APIs.&#39; &quot; src=&quot;https://j0.lol/blog/understanding-webgpu/ZFi2Fx8vva-2064.jpeg&quot; width=&quot;2064&quot; height=&quot;496&quot;&gt;&lt;/picture&gt;

    &lt;figcaption&gt;A timeline of notable Graphics APIs.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
WebGL is a simple idea: Take OpenGL ES, a subset of OpenGL, and surface the API in the browser. When a user makes a call, just use the OS&#39;s OpenGL. This is what the four big browser engines (Opera, Chrome, Firefox, Safari) did in 2011.
&lt;/p&gt;

&lt;p&gt;The big conflict here is that just a few years later, OpenGL was set to be replaced. The key change between these new APIs and OpenGL was the idea of &quot;low driver overhead.&quot; The idea is that by making more of an effort to conform to how the GPU is structured, you can talk to the GPU using less work on the CPU. In practice, this means you have to set up a lot of things before you can start GPU work.&lt;/p&gt;

&lt;p&gt;As part of this replacement, Apple deprecated OpenGL in 2018. This raises some questions over how WebGL works, right? The entire point of it was to be a wrapper over a now deprecated API. Thankfully Chrome thought of this, all the way in 2010! They created the ANGLE project to translate OpenGL ES to other graphics APIs. Nowadays, WebGL support looks like this:&lt;/p&gt;

&lt;figure style=&quot;background-color: rgb(249 250 251)&quot;&gt;
&lt;img alt=&quot;A diagram showing how WebGL gets interpreted in various browsers. WebGL is turned into OpenGL ES. Through ANGLE on Firefox, Chrome, and Safari, OpenGL ES is turned into DirectX 12, Vulkan, Metal, and OpenGL.&quot; src=&quot;https://vps.j0.lol/website-assets/shapes%20at%2025-12-19%2019.30.23.svg&quot;&gt;
&lt;figcaption&gt;All browsers seem to use ANGLE for this.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;A Better Way&lt;/h2&gt;

&lt;p&gt;Opening up these new APIs to the browser directly was the obvious next step. However, we need to solve the mismatch between what the browser wants and what the operating system wants. Lets say, for example, we pick Vulkan as the graphics API for the browser. Great! But now we have to solve how to run Vulkan on macOS. Because the people who make the browsers (Google, Apple, Mozilla) are mostly the same people who made the operating systems (Google, Apple, Microsoft) they would have a hard time picking a favorite API out of the current available ones. To solve this, a new API had to be made. And that&#39;s what WebGPU is.&lt;/p&gt;

&lt;p&gt;The sad part about this is that we basically need another ANGLE to turn WebGPU into native OS APIs. But because WebGPU was designed for this, it maps much closer to current APIs than WebGL does. That makes developing a new ANGLE easier, and the closer the &quot;mapping&quot; is to the native API, the less performance is lost. Here&#39;s what those mappings look like:&lt;/p&gt;

&lt;figure style=&quot;background-color: rgb(249 250 251)&quot;&gt;
&lt;img alt=&quot;A diagram showing how WebGPU gets interpreted in various browsers. On Chrome it uses Dawn to turn WebGPU into every other API. On Firefox it uses WGPU to turn WebGPU into every other API. On Safari, it directly converts into Metal. It is noted that while we don&#39;t know exactly what Apple uses to translate WebGPU to Metal, it is likely a thin translation layer. This is because Metal and WebGPU are very similar APIs.&quot; src=&quot;https://vps.j0.lol/website-assets/shapes%20at%2025-12-19%2018.12.57.svg&quot;&gt; &lt;figcaption&gt;Dawn and WGPU are the &quot;ANGLEs&quot; of WebGPU.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;h2&gt;What This Means For GPU Programming&lt;/h2&gt;

&lt;p&gt;Essentially, WebGPU is not bound to the web browser. It is the new &quot;universal standard&quot; for talking to the GPU. This has many possible applications. Let&#39;s say you&#39;re making a game engine, for example. You want to target every system possible, because you don&#39;t want to lock down game developers to using, say, Linux on Desktop only. If you were to use WebGPU instead of another graphics API, you would only have to write your rendering code once!&lt;/p&gt;

&lt;figure style=&quot;background-color: rgb(249 250 251)&quot;&gt;
&lt;img alt=&quot;A joke diagram referencing the previous flowchart. In this chart, all of the graphics APIs are replaced with WebGPU.&quot; src=&quot;https://vps.j0.lol/website-assets/shapes%20at%2025-12-19%2019.47.03.svg&quot;&gt; &lt;figcaption&gt;You could definitely simplify this.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This isn&#39;t really a hypothetical. Today, you can use &lt;a href=&quot;https://crates.io/crates/wgpu&quot;&gt;WGPU&lt;/a&gt; to talk to the GPU natively on any system. That&#39;s why I&#39;m so excited about WebGPU.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>I Made a DSL for My Blog</title>
    <link href="https://j0.lol/blog/ma-kup/" />
    <updated>2025-11-27T00:00:00Z</updated>
    <id>https://j0.lol/blog/ma-kup/</id>
    <content type="html">
&lt;p&gt;
I think I have spent most of my time on this blog trying to figure out how to write posts better. I honestly think I have outdone myself this time.
&lt;/p&gt;

&lt;figure style=&quot;background-color: white&quot;&gt;
&lt;!-- Image Map Generated by http://www.image-map.net/ --&gt;
&lt;img width=&quot;595&quot; height=&quot;186&quot; src=&quot;https://j0.lol/blog/ma-kup/tyCLPLbDMg-595.svg&quot; alt=&quot;A diagram. Markdown to PHP to HTML to Makup. An arrow is pointing to Makup saying &#39;you are here&#39;.&quot;&gt;

&lt;figcaption&gt; What I have used as blog post formats. &lt;br&gt;&lt;a href=&quot;https://j0.lol/blog/php-site&quot;&gt;Markdown&lt;/a&gt; &amp;rarr; &lt;a href=&quot;https://j0.lol/blog/downtime&quot;&gt;PHP&lt;/a&gt; &amp;rarr; &lt;a href=&quot;https://j0.lol/blog/rewrite-v5&quot;&gt;HTML&lt;/a&gt; &amp;rarr; &lt;a href=&quot;https://j0.lol/blog/ma-kup/&quot;&gt;Makup&lt;/a&gt;. &lt;br&gt;Click each entry to go to their blog posts.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&#39;ve been constantly changing because I have been unsatisfied with everything I&#39;ve tried. I switched to a HTML-style format so I could have more control over my formatting. I&#39;ve been trying to balance control over formatting with lack of verbosity.&lt;/p&gt;

&lt;p&gt;
So here&#39;s the language:
&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is a sample paragraph.&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;$code-block lang=&amp;quot;html&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;span&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;This is a sample code block.&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;span&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;$code-block&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
&lt;p&gt;I&#39;ve changed the load-bearing symbol from &lt;code&gt;#&lt;/code&gt; to &lt;code&gt;$&lt;/code&gt;, for maybe obvious reasons. 

&lt;/p&gt;&lt;p&gt;I titled it &lt;em&gt;makup&lt;/em&gt; because I&#39;m terrible at naming things. It&#39;s just &lt;em&gt;markup&lt;/em&gt; without the &quot;r&quot;. Or maybe &lt;em&gt;makeup&lt;/em&gt; with the &quot;e&quot;. I dunno.&lt;/p&gt;

&lt;h2&gt;What it Does&lt;/h2&gt;

&lt;p&gt;Makup (the &lt;em&gt;compiler&lt;/em&gt;) parses makup &lt;em&gt;language&lt;/em&gt;, then passes any named components (eg &lt;code&gt;&amp;lt;$code-block&amp;gt;&lt;/code&gt;) to a lookup table. If the tag name is registered, it will pass the contents and attributes of the component to a function. &lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;hello&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;input&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;attrs&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-t&gt;HashMap&lt;/a-t&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;&amp;gt;)&lt;/a-p&gt; -&amp;gt; &lt;a-t&gt;String&lt;/a-t&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-m&gt;format!&lt;/a-m&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;&amp;lt;span&amp;gt;Hello {input}&amp;lt;/span&amp;gt;&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;The outputted string will replace the component. In theory, this language does not really need to work on top of HTML (it&#39;s similar to PHP in that way) but the syntax is trying to mimic it.&lt;/p&gt;

&lt;h3&gt;How I Made it&lt;/h3&gt;

&lt;p&gt;The language is specified using &lt;a href=&quot;https://pest.rs/&quot;&gt;Pest&lt;/a&gt;, a PEG parser (and generator). Having only worked with LL/LALR parsers before, it was a little strange to work with, but the grammar is quite simple.&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					
					&lt;span class=&quot;filename code-filename-tag&quot;&gt;grammar.pest&lt;/span&gt;
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;document = { SOI ~ (statement | text)* ~ EOI }

statement = { tag_open ~ text ~ tag_close }
tag_open  = { &amp;quot;&#92;&amp;lt;&#92;$&amp;quot; ~ PUSH(tag_ident) ~ attr_list? ~ &amp;quot;&amp;gt;&amp;quot; }
tag_ident = { (ASCII_ALPHA_LOWER | &amp;quot;-&amp;quot; | &amp;quot;_&amp;quot;)+ }
tag_close = { &amp;quot;&amp;lt;/$&amp;quot; ~ POP ~ &amp;quot;&amp;gt;&amp;quot; }

attr_list = { &amp;quot; &amp;quot;+ ~ attr ~ (&amp;quot; &amp;quot; ~ attr)* ~ &amp;quot; &amp;quot;* }
attr      = { attr_key ~ &amp;quot;=&amp;quot; ~ &amp;quot;&#92;&amp;quot;&amp;quot; ~ quoted_text ~ &amp;quot;&#92;&amp;quot;&amp;quot; }
attr_key  = { (ASCII_ALPHA_LOWER | &amp;quot;-&amp;quot; | &amp;quot;_&amp;quot;)+ }

quoted_text = { (!(&amp;quot;&amp;lt;$&amp;quot; | &amp;quot;&amp;lt;/$&amp;quot; | &amp;quot;&#92;&amp;quot;&amp;quot;) ~ ANY)+ }
text        = { (!(&amp;quot;&amp;lt;$&amp;quot; | &amp;quot;&amp;lt;/$&amp;quot;) ~ ANY)+ }&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;The whole library is less than 100 lines of code (excluding generated code and tests.) You can view the &quot;core&quot; of it &lt;a href=&quot;https://j0.lol/blog/ma-kup/&quot;&gt;on Tangled, here&lt;/a&gt;. It&#39;s a bit ugly right now. I&#39;m not sure if I could get it to be nicer, but I&#39;m just glad it works robust enough to write blog posts in.&lt;/p&gt;

&lt;h2&gt;Using the library&lt;/h2&gt;

&lt;p&gt;Honestly, the main reason I wanted to work on this is... code block highlighting sucks. What&#39;s nice about this approach is that I can finally render them on the server instead of the client&lt;a ref=&quot;csr&quot;&gt;&lt;/a&gt;., with a robust parser (tree-sitter). To do this, I&#39;m using the library &lt;a href=&quot;https://crates.io/crates/autumnus&quot;&gt;&lt;code&gt;autumnus&lt;/code&gt;&lt;/a&gt;. I was going to use the &lt;a href=&quot;https://crates.io/crates/inkjet&quot;&gt;&lt;code&gt;inkjet&lt;/code&gt;&lt;/a&gt; crate, but it got deprecated days after I found it. Welp.&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-c&gt;// SAFETY: function is not allowed to error. Annoyingly.&lt;/a-c&gt;
&lt;a-at&gt;#&lt;/a-at&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-at&gt;allow&lt;/a-at&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-at&gt;clippy&lt;/a-at&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-at&gt;expect_used&lt;/a-at&gt;&lt;a-p&gt;,&lt;/a-p&gt;&lt;a-at&gt; clippy&lt;/a-at&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-at&gt;unwrap_used&lt;/a-at&gt;&lt;a-p&gt;)]&lt;/a-p&gt;
&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;code_block&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;contents&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;attrs&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-t&gt;HashMap&lt;/a-t&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;&amp;gt;)&lt;/a-p&gt; -&amp;gt; &lt;a-t&gt;String&lt;/a-t&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; lang = attrs
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;lang&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;or_else&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;|| attrs&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;language&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;))&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;unwrap_or&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-s&gt;&amp;quot;plain&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

    &lt;a-k&gt;let&lt;/a-k&gt; formatter = autumnus&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-t&gt;HtmlInlineBuilder&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;new&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;lang&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-t&gt;Language&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;guess&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;lang&lt;a-p&gt;,&lt;/a-p&gt; contents&lt;a-p&gt;))&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;source&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;contents&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;theme&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;Some&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;
            autumnus&lt;a-p&gt;::&lt;/a-p&gt;themes&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;get&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;catppuccin_mocha&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;expect&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;Built in!&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;),&lt;/a-p&gt;
        &lt;a-p&gt;))&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;build&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;expect&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;i hope this doesnt crash!&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

    &lt;a-k&gt;let&lt;/a-k&gt; &lt;a-k&gt;mut&lt;/a-k&gt; output = &lt;a-t&gt;Vec&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;new&lt;/a-f&gt;&lt;a-p&gt;();&lt;/a-p&gt;
    formatter&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;format&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-k&gt;mut&lt;/a-k&gt; output&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap&lt;/a-f&gt;&lt;a-p&gt;();&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; output = &lt;a-t&gt;String&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;from_utf8&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;output&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap&lt;/a-f&gt;&lt;a-p&gt;();&lt;/a-p&gt;

    maud&lt;a-p&gt;::&lt;/a-p&gt;html! &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;code-block &lt;a-p&gt;{&lt;/a-p&gt;
            &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;code&lt;/a-pr&gt;-&lt;a-f&gt;language&lt;/a-f&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-p&gt;(&lt;/a-p&gt;lang&lt;a-p&gt;)&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
            &lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;PreEscaped&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;output&lt;a-p&gt;))&lt;/a-p&gt;
        &lt;a-p&gt;}&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
    &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;into_string&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;As you can see, there&#39;s still some papercuts with Makup, but it&#39;s perfectly usable for this. (Unwrapping here is not catastrophic, it&#39;s currently expected that the parsing will panic in certain cases.) I might try and add &lt;a href=&quot;https://maud.lambda.xyz/&quot;&gt;&lt;code&gt;maud&lt;/code&gt;&lt;/a&gt; support directly so i don&#39;t have to call a conversion method, but trait trickery like that in Rust is maybe somewhat above my skill level.&lt;/p&gt;

The other use case I had for this was to render the speech-boxes that I use to add a bit of personality to my writing. In this case, I already had a function that generated them for non-post pages on my website. That means it was as simple as plugging that into my rewriter function:


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;speech_box&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;contents&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;attrs&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-t&gt;HashMap&lt;/a-t&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;str&lt;/a-t&gt;&lt;a-p&gt;&amp;gt;)&lt;/a-p&gt; -&amp;gt; &lt;a-t&gt;String&lt;/a-t&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; char = attrs&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;character&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap_or&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-s&gt;&amp;quot;deer&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; emotion = attrs&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;emotion&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap_or&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-s&gt;&amp;quot;neutral&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

    &lt;a-c&gt;// ⤵︎ this function!&lt;/a-c&gt;
    &lt;a-f&gt;speech&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;
        &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-k&gt;match&lt;/a-k&gt; &lt;a-o&gt;*&lt;/a-o&gt;char &lt;a-p&gt;{&lt;/a-p&gt;
            &lt;a-s&gt;&amp;quot;you&amp;quot;&lt;/a-s&gt; =&amp;gt; &lt;a-t&gt;SpeechCharacter&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;You&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
            _ =&amp;gt; &lt;a-t&gt;SpeechCharacter&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Deer&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
        &lt;a-p&gt;},&lt;/a-p&gt;
        &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-k&gt;match&lt;/a-k&gt; &lt;a-o&gt;*&lt;/a-o&gt;emotion &lt;a-p&gt;{&lt;/a-p&gt;
            &lt;a-s&gt;&amp;quot;worried&amp;quot;&lt;/a-s&gt; =&amp;gt; &lt;a-t&gt;SpeechEmotion&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Worried&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
            &lt;a-s&gt;&amp;quot;shocked&amp;quot;&lt;/a-s&gt; =&amp;gt; &lt;a-t&gt;SpeechEmotion&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Shocked&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
            &lt;a-s&gt;&amp;quot;happy&amp;quot;&lt;/a-s&gt; =&amp;gt; &lt;a-t&gt;SpeechEmotion&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Happy&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
            _ =&amp;gt; &lt;a-t&gt;SpeechEmotion&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Neutral&lt;/a-cr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
        &lt;a-p&gt;},&lt;/a-p&gt;
        &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-m&gt;html!&lt;/a-m&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;PreEscaped&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;contents&lt;a-p&gt;))&lt;/a-p&gt; &lt;a-p&gt;},&lt;/a-p&gt;
    &lt;a-p&gt;)&lt;/a-p&gt;
    &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;into_string&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
That&#39;s all. If anyone would be interested in using this library for anything, please reach out. I would love to work on it further, given the chance &lt;a ref=&quot;blabla&quot;&gt;&lt;/a&gt;.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;h2&gt;Footnotes&lt;/h2&gt;

&lt;ol&gt;
&lt;li id=&quot;note_csr&quot;&gt;I&#39;ve probably talked about this already on here, but the client side rendering was pretty horrible. When I was using Prism and Shiki, I used a special comments syntax for having unescaped markup in code blocks. When first loading the page, an empty code block would be shown, and then once the highlighter kicked in it would be swapped for a pretty code block. The &lt;em&gt;one thing&lt;/em&gt; I currently miss from Shiki is the ability to have two themes and swap them with device color scheme. I believe this is possible with autumnus, but it would have to be a bit hacky. I&#39;ll &lt;a href=&quot;https://github.com/leandrocp/autumnus/issues/253&quot;&gt;open an issue now&lt;/a&gt; on the repo and see what happens.&lt;/li&gt;
&lt;li id=&quot;note_blabla&quot;&gt;I&#39;m definitely not &lt;em&gt;done&lt;/em&gt; working on it. I want to eventually port over the footnotes system, but that would require making my rewriter a bit more stateful (and adding void tags!) Specifically, I would need some shared state &lt;em&gt;somewhere&lt;/em&gt; so footnotes can be ordered correctly and backreferences can be made. The current version of this is in &lt;a href=&quot;https://tangled.org/j0.lol/bog/blob/trunk/static/js/footnotes.js&quot;&gt;a quick JS script&lt;/a&gt; I made.&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>should we be evangelizing the way we do</title>
    <link href="https://j0.lol/blog/thoughts-on-tech-evangelism/" />
    <updated>2025-11-25T00:00:00Z</updated>
    <id>https://j0.lol/blog/thoughts-on-tech-evangelism/</id>
    <content type="html">
&lt;blockquote&gt;
&lt;span style=&quot;font-weight: bold; font-size: 1.5em;&quot;&gt;Verb&lt;/span&gt;&lt;br&gt;

&lt;strong&gt;evangelize&lt;/strong&gt; &lt;i&gt;(third-person singular simple present &lt;a href=&quot;https://en.wiktionary.org/wiki/evangelizes#English&quot;&gt;evangelizes&lt;/a&gt;, present participle &lt;a href=&quot;https://en.wiktionary.org/wiki/evangelizing#English&quot;&gt;evangelizing&lt;/a&gt;, simple past and past participle &lt;a href=&quot;https://en.wiktionary.org/wiki/evangelized#English&quot;&gt;evangelized&lt;/a&gt;)&lt;/i&gt;
&lt;ol&gt;
 &lt;li&gt;   To tell people about (a particular branch of) &lt;a href=&quot;https://en.wiktionary.org/wiki/Christianity#English&quot;&gt;Christianity,&lt;/a&gt; especially in order to &lt;a href=&quot;https://en.wiktionary.org/wiki/convert#English&quot;&gt;convert&lt;/a&gt; them; to &lt;a href=&quot;https://en.wiktionary.org/wiki/preach#English&quot;&gt;preach&lt;/a&gt; the &lt;a href=&quot;https://en.wiktionary.org/wiki/gospel#English&quot;&gt;gospel&lt;/a&gt; to.

&lt;/li&gt;&lt;li&gt;To preach any ideology to those who have not yet been converted to it.

 &lt;/li&gt;&lt;li id=&quot;define&quot;&gt;&lt;mark&gt;   To be &lt;a href=&quot;https://en.wiktionary.org/wiki/enthusiastic#English&quot;&gt;enthusiastic&lt;/a&gt; about something, and to attempt to share that enthusiasm with others; to &lt;a href=&quot;https://en.wiktionary.org/wiki/promote#English&quot;&gt;promote&lt;/a&gt;.&lt;/mark&gt;
&lt;/li&gt;&lt;/ol&gt;

&lt;cite&gt;— &lt;i&gt;&lt;a href=&quot;https://en.wiktionary.org/wiki/evangelize&quot;&gt;evanglize - Wiktionary&lt;/a&gt;&lt;/i&gt;&lt;/cite&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was just thinking about Tech Evangelism. As a technologist, I am often to subject to the opinions of my peers
I have some opinions of myself.&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
It looks like &lt;a href=&quot;https://en.wikipedia.org/wiki/Technology_evangelist&quot;&gt;Tech Evangelist&lt;/a&gt; is a real term. It seems to mostly apply to people trying to advertise products, but it seems like the definition I am working with still fits. To make it clear, I mean people personally (and mostly solo) advocating for things they like. Not somebody being paid to do such work.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt; Here:
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Editor: Either Helix or Zed right now. I switch between them based on context.
&lt;/li&gt;&lt;li&gt;Programming Language: Rust.
&lt;/li&gt;&lt;li&gt;OS: currently macOS, but my second pick would be NixOS. Probably.
&lt;/li&gt;&lt;/ul&gt;

&lt;p&gt;
I have formed these opinions from life experience. I have used a lot of editors, so I can somewhat confidently say that these editors fit me most. I have spent a lot of time with Rust and mostly enjoy working with it. I tolerate macOS the most out of the current options right now.
&lt;/p&gt;


&lt;p&gt;
If you have been in programmer circles a lot, you will hear endless debate about programming language, or editor, or sometimes OS. I would like to question what purpose this serves in these spaces.
&lt;/p&gt;

&lt;h2&gt;Why we might evangelize&lt;/h2&gt;
&lt;h3&gt;People like sharing their opinions&lt;/h3&gt;

&lt;p&gt;
I think this is a big one. People share opinions because its fun to share opinions, and engage in some sort of dialogue about the things you use. Uh. I&#39;m not sure really why though.
&lt;/p&gt;

&lt;h3&gt;Searching for connection?&lt;/h3&gt;

&lt;p&gt;We are a social species. If we share the same opinions about something, maybe it says something about a shared personality trait. For example, maybe if we both use Vim, we share some sort of hacker spirit.&lt;/p&gt;

&lt;h3&gt;Sharing joy?&lt;/h3&gt;

&lt;p&gt;Maybe we just remember the good times of using a tool, and want to spread that with others. I think the core of this is that using good tools makes us excited (or &lt;em&gt;enthusiatic&lt;/em&gt;, like in the &lt;a href=&quot;https://j0.lol/blog/thoughts-on-tech-evangelism/#define&quot;&gt;above definition&lt;/a&gt;) and we want to share how that made us feel with our peers.&lt;/p&gt;

&lt;h2&gt;What&#39;s wrong with evangelizing?&lt;/h2&gt;

&lt;p&gt;I think a lot of communities in tech are based on exclusion. Take, for example, the ideal of an Arch Linux user. If I were to describe the &quot;popular culture&quot; stereotype of an Arch Linux user, it would be that they are annoying and want to force their opinions onto you. This is a reductive way to talk about people who use a tool, but this was born from exclusion.&lt;/p&gt;


&lt;h3&gt;Mindful evangelism&lt;/h3&gt;
&lt;p&gt;This is why I try to practice being mindful when evangelizing to others.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Try and lead with why your tool is better, but not needlessly disparaging their tool.
&lt;/li&gt;&lt;li&gt;Argue your point in good faith. This means listening to the concerns of the other person.
&lt;/li&gt;&lt;li&gt;Offer some vulnerability in your argument. If you have used a tool a lot, you know it has downsides, so present them too.
&lt;/li&gt;&lt;li&gt;Try and learn something from the other person too. You don&#39;t need to do the exact same things they do to connect with them. They likely know something about their tool that you don&#39;t, as well.
&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;These should be pretty obvious, I think.&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
I suck at ending blog posts, sorry! Just remember that making friends and having fun together is better than winning any online turf war. &lt;br&gt;&lt;small&gt;Also, this post felt a bit less &lt;em&gt;professional&lt;/em&gt; than the other ones, so that&#39;s why I&#39;m not using &lt;em&gt;Title Case&lt;/em&gt; in my headings. &lt;br&gt;&lt;small&gt;Also also, wow, my &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; styling is not that great. I should change it up. Plus, it doesn&#39;t even work with &lt;code&gt;&amp;lt;cite&amp;gt;&lt;/code&gt;!&lt;/small&gt;&lt;/small&gt;
&lt;/p&gt;
		&lt;/aside&gt;

</content>
  </entry>
  <entry>
    <title>Squircles in CSS!</title>
    <link href="https://j0.lol/blog/corner-shape/" />
    <updated>2025-10-22T00:00:00Z</updated>
    <id>https://j0.lol/blog/corner-shape/</id>
    <content type="html">&lt;script src=&quot;https://cdn.jsdelivr.net/npm/baseline-status@1/baseline-status.min.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;
&lt;baseline-status featureId=&quot;corner-shape&quot;&gt;&lt;/baseline-status&gt;

&lt;p&gt;
		Just wanted to shout out this cool new feature coming to CSS: you can now specify &lt;em&gt;exactly how round&lt;/em&gt; you want your &lt;code&gt;border-radii&lt;/code&gt; to be.
&lt;/p&gt;

&lt;p&gt;A normal radius would look like this:&lt;/p&gt;

&lt;style&gt;
:root {
		--circle-size: 3em;
}
.box {
		position: relative;
		display: block;
		color: black;
		background-color: white;
		padding: var(--circle-size);
		border-radius: 0 var(--circle-size) 0 var(--circle-size);
		border: 1px solid black;
		width: calc(var(--circle-size) * 5);
		box-shadow: 1px 1px white,-1px -1px white,-1px 1px white,1px -1px white;
}
.circle {
		position: absolute;
		top: 0;
		right: 0;
		padding: var(--circle-size);
		border: 1px solid #f00;
		z-index: 9;
		border-radius: var(--circle-size);
}
&lt;/style&gt;
&lt;center&gt;
&lt;div class=&quot;box&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle&quot;&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/center&gt;

&lt;p&gt;The border corners are just arcs of circles. This is what the new property looks like:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; notch&lt;a-p&gt;;&lt;/a-p&gt;    &lt;a-c&gt;/* or superellipse(-infinity) */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; scoop&lt;a-p&gt;;&lt;/a-p&gt;    &lt;a-c&gt;/* or superellipse(-1) */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; bevel&lt;a-p&gt;;&lt;/a-p&gt;    &lt;a-c&gt;/* or superellipse(0), a straight line */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; round&lt;a-p&gt;;&lt;/a-p&gt;    &lt;a-c&gt;/* or superellipse(1), default. */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; squircle&lt;a-p&gt;;&lt;/a-p&gt; &lt;a-c&gt;/* or superellipse(2) */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; square&lt;a-p&gt;;&lt;/a-p&gt;   &lt;a-c&gt;/* or superellipse(infinity) */&lt;/a-c&gt;

&lt;a-c&gt;/* You can also specify each corner separately. */&lt;/a-c&gt;
&lt;a-pr&gt;corner-shape&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; square squircle bevel scoop&lt;a-p&gt;;&lt;/a-p&gt;  &lt;a-c&gt;/* top-left, top-right, bottom-right, bottom-left */&lt;/a-c&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;style&gt;

.supports-corner-shape {
		display: none;
}

.not-supports-corner-shape {
		display: initial;
}

@supports (corner-shape: squircle) {
		.supports-corner-shape {
				display: initial;
		}

		.not-supports-corner-shape {
				display: none;
		}
}


.container {
	display: flex;
	flex-flow: row wrap;
	gap: 1em;
	width: 40em;

}
	.boxcontainer {
		display: flex;
		flex-flow: column nowrap;
		align-items: center;
	}

.box-2 {
		position: relative;
		display: block;
		color: black;
		background-color: white;
		padding: var(--circle-size);
		border-radius: 0 var(--circle-size) 0 var(--circle-size);
		border: 1px solid black;
		width: calc(var(--circle-size) * 4);
		box-shadow: 1px 1px white,-1px -1px white,-1px 1px white,1px -1px white;
}

.circle-2 {
		position: absolute;
		corner-shape: inherit;
		top: 0;
		right: 0;
		padding: var(--circle-size);
		border: 1px solid #f00;
		z-index: 9;
		border-radius: var(--circle-size);
}
&lt;/style&gt;

&lt;p&gt; Lined up, they look like this:&lt;/p&gt;

&lt;div class=&quot;supports-corner-shape&quot;&gt;
		&lt;center&gt;
		&lt;span&gt;Your browser supports &lt;code&gt;corner-shape&lt;/code&gt;.&lt;/span&gt;
				&lt;div class=&quot;container&quot;&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: notch;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Notch&lt;/div&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: scoop;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Scoop&lt;/div&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: bevel;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Bevel&lt;/div&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: round;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Round&lt;/div&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: squircle;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Squircle&lt;/div&gt;
					&lt;div class=&quot;boxcontainer&quot;&gt;&lt;div class=&quot;box-2&quot; style=&quot;corner-shape: square;&quot;&gt; &amp;nbsp; &lt;div class=&quot;circle-2&quot;&gt;&lt;/div&gt;&lt;/div&gt;Square&lt;/div&gt;
				&lt;/div&gt;
		&lt;/center&gt;
&lt;/div&gt;

&lt;div class=&quot;not-supports-corner-shape&quot;&gt;
&lt;center&gt;
&lt;span&gt;Your browser does not support &lt;code&gt;corner-shape&lt;/code&gt;. A rendered image is provided.&lt;/span&gt;
&lt;/center&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/corner-shape/iJ5zSfnAYM-598.webp 598w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/corner-shape/iJ5zSfnAYM-598.jpeg&quot; alt=&quot;a few diagrams of each corner shape&quot; height=&quot;282&quot; width=&quot;598&quot;&gt;&lt;/picture&gt;
&lt;/div&gt;
&lt;p&gt;You might want to use this effect for mimicking an iOS-style squircle&lt;a ref=&quot;squircle&quot;&gt;&lt;/a&gt;, since previous ways of doing this had to use SVG masks&lt;a ref=&quot;svgs&quot;&gt;&lt;/a&gt; (Here&#39;s &lt;a href=&quot;https://codepen.io/AmeliaBR/details/KaJaWK/&quot;&gt;the CodePen&lt;/a&gt;.) If you want to see more interesting ways of using this new feature, see &lt;a href=&quot;https://css-tricks.com/what-can-we-actually-do-with-corner-shape/&quot;&gt;CSS-Tricks article&lt;/a&gt; about it. (I think we might have a good replacement for the classic &lt;a href=&quot;https://css-tricks.com/snippets/css/css-triangle/&quot;&gt;CSS Triangle&lt;/a&gt;!)&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
Squircles, and other superellipses, are a real thing, not just a silly name. MDN has &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/superellipse#description&quot;&gt;a good explanation&lt;/a&gt; on this.
&lt;/p&gt;
		&lt;/aside&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape&quot;&gt;&lt;code&gt;corner-shape&lt;/code&gt;&lt;/a&gt; (MDN)
		&lt;ul&gt;&lt;li&gt;This page has a &lt;a href=&quot;https://developer.mozilla.org/en-US/play?uuid=e0ee56b846f8957e182948536e43988592a49464&amp;state=lVbbbuM2EP2VKbvFOkXkS1IDrSwbbRdbbNHtS7coUIAPocSRxF2a1JKUYyHNvxckpUS%2BJEgeBNLkOWeGM8Oh70jttpKkJCu12W6oAsi42IUJQCZZjhJKbdaUFNooNImtWYNJUWtRICWbd7XWFoHBeBt2TLaYZrPAH8QsSiwcCP6kWAQCZLpxldFtA0FgTckf2N1qw%2B0IFGFCq2htTYn92jLjdeIE%2FgPbNmhQStFYnAhVCiVcd5HNIvGMVPQR%2BVhTmEL2qmF6rHv1nOCgY3SrOCWbMB4rLF6ikOMOJSWbMB4rzF%2BiYAutG38QPx4rJC9yQmlX1JRswngi8XSEw0JI6TNJ%2Fq1VhWedzfLmwNT1M84eIhfT5Yux81dgk1eBX%2BNFcuZwJ%2FHLZrFU45WdDXf2qctrGBetTawUHM342ubacDRJ3D9%2Fb4VqWjc44romlDNTFVIyrIY7fWRj2NsKtaZk%2Frgw1NIPyxGI7deU%2FDRCWYfNmpIFJTA7OGQ261tVZjGUy8bHoujDRS5JYS1JQ1%2BDO08stXJJybZCdilQ8gHlDp0oGCWXQMkvRjDpp5Ypm1g0olxRdU8VVbnmXZS4FdzVKZTCJYVWDpVb%2BeUtM5VQKVzNmz2w1umB2fsTyVzYRrIuhVLiPvA%2Bt9aJshu0UihQOTRhj0lRqUQ43NqD9WgrcbqJ9h5Nha4aLDWMc6GqFK6bPSwfMT5gwMUuVa5OdJn4JE6uLiJpLBw5py6fHqqPyGI%2B%2Fy4wahRV7VJY%2FDjvNXJWfKlCu0sKLbVJodKSH2%2BJLaswBSkUMpNUvoZQuUmsAqch187p7WX8bap8crVcwvDNYH7x9NZ0eeH3Lgb%2Ff7Zt02jjLCjtYDJ%2BglIIPbGPyGPefTpkCq2y6OAbsfV01ic%2FaEZwmuZYaoMD6SGvlPyrWwO50bcWDXCN0XjvCbga4e3YkbfQGN2gcd2UktWgFqKXS1Z86ZdOY%2FttWRb8%2Brrff8hfLvUD6ThlAA73LgkFd1Bqo0JaGNzC%2FPi8sIHvh7M%2BmFJa4QC8P6qWXO%2F9%2Bbi%2BTWHR7MPna7QyrIvpIZfks7%2B2hVbWgcHCMVVJhDVwXbRbVG76tUXTfQrlrs2Ekt4CJT7DkddfhmdJfjrmhFY2plTo3kv001%2B73%2FnkpLF5KlVl%2F1iBRfcuZNBO%2Bvp58H5qXSdxGhP8KfwzWvc%2BTkMXDPGKbuTmH78Ca7h5cxd8ipD7Zn%2BzOqcaO%2FdfsXGvB%2F4RVCiF5sPff370uuFxyArNcXNY%2FG%2Fuxk7dr7JZAGW56eEHr4TH99YeoaE53xw2pSnj%2FP0OlfsorEOFPv5FHV%2BOy1HcfEDjic8Q4utzgh9HfRXKx9W4RZIS6RsRuf8f&amp;srcPrefix=%2Fen-US%2Fdocs%2FWeb%2FCSS%2Fcorner-shape%2F&quot;&gt;good example&lt;/a&gt; that demonstrates this property.&lt;/li&gt;&lt;/ul&gt;
		&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/superellipse&quot;&gt;&lt;code&gt;superellipse()&lt;/code&gt;&lt;/a&gt; (MDN)&lt;/li&gt;

		&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/what-can-we-actually-do-with-corner-shape/&quot;&gt;What Can We Actually Do With corner-shape?&lt;/a&gt; (CSS-Tricks, mentioned above)&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Making embed images with Typst</title>
    <link href="https://j0.lol/blog/opengraph-images/" />
    <updated>2025-10-21T22:00:00Z</updated>
    <id>https://j0.lol/blog/opengraph-images/</id>
    <content type="html">&lt;p&gt;
Just making a quick post to show off new functionality. I saw a couple websites using Typst to generate OpenGraph embed images. I basically took the implementation from &lt;a href=&quot;https://github.com/rust-lang/crates_io_og_image&quot;&gt;crates.io&#39;s &lt;code&gt;crates_io_og_image&lt;/code&gt;&lt;/a&gt; library. (It&#39;s MIT licensed, I&#39;m allowed to!)
&lt;/p&gt;

&lt;p&gt;One thing I noticed: invoking Typst is &lt;em&gt;slow slow&lt;/em&gt;. I ended up having to add caching to keep rendered images around.&lt;/p&gt;

&lt;p&gt;Another issue was that Discord embeds were, like, wrong? Well first, they looked like this:&lt;/p&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/opengraph-images/mmlEU1NAdQ-940.webp 940w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/opengraph-images/mmlEU1NAdQ-940.jpeg&quot; alt=&quot;Screenshot of discord&quot; width=&quot;940&quot; height=&quot;214&quot;&gt;&lt;/picture&gt;

&lt;p&gt;I looked at MDN&#39;s embed &lt;code&gt;meta&lt;/code&gt; tags, and saw that they used this: &lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;meta&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;og:image:type&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;content&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;image/png&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;meta&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;og:image:height&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;content&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;1080&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;meta&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;og:image:width&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;content&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;1920&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;meta&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;og:image:alt&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;content&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background.&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;I thought that maybe it couldn&#39;t determine the size, so used a small version? So I updated my site, and it still wasn&#39;t working. So I looked again, and noticed this rather unassuming tag:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;meta&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;twitter:card&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;content&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;summary_large_image&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-worried&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;*sigh* Why is Twitter here?? Do I have to add this to my website?&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;I guess so. I went to Discord&#39;s embed tester, and it explicitly says that they use Twitter embed stuff. I suppose this stuff isn&#39;t really specified. I feel like OpenGraph should add something like this eventually.&lt;/p&gt;

&lt;p&gt;Anyway, I added all of this stuff and it worked. Yaaay. &lt;/p&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/opengraph-images/WfwVRkQ7iw-892.webp 892w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/opengraph-images/WfwVRkQ7iw-892.jpeg&quot; alt=&quot;Screenshot of discord&quot; width=&quot;892&quot; height=&quot;612&quot;&gt;&lt;/picture&gt;

 &lt;p&gt;Here&#39;s a sample embed:&lt;/p&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/opengraph-images/4a_FDjXbcO-1200.webp 1200w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/opengraph-images/4a_FDjXbcO-1200.jpeg&quot; alt=&quot;an embed image&quot; width=&quot;1200&quot; height=&quot;630&quot;&gt;&lt;/picture&gt;

&lt;p&gt;There&#39;s a chance that I could accidentally mess this up if I put a big title or subtitle in. That would be pretty funny.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Web Components Are Easy, Mostly</title>
    <link href="https://j0.lol/blog/webcomponentsforfunandprofit/" />
    <updated>2025-10-17T19:00:00Z</updated>
    <id>https://j0.lol/blog/webcomponentsforfunandprofit/</id>
    <content type="html">
&lt;p&gt;
		Web components are a tool you can use to give layout and functionality to &lt;em&gt;custom elements&lt;/em&gt;, e.g. &lt;code&gt;&amp;lt;hello-world&amp;gt;&lt;/code&gt;. There are, roughly, three types of web components. Most web component frameworks (Lit) are using &lt;em&gt;Imperative Shadow DOM&lt;/em&gt; web components.
&lt;/p&gt;

&lt;p&gt;
		Let me break that down. Imperative means you create and modify elements through function calls instead of declaring a layout (e.g. in HTML). DOM is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_(abstract_data_type)&quot;&gt;tree model&lt;/a&gt; of the layout (HTML). A shadow DOM is a separate tree, which means it is somewhat encapsulated from the rest of the document (the Light DOM.)
&lt;/p&gt;

&lt;figure&gt;
		&lt;img width=&quot;535&quot; height=&quot;326&quot; style=&quot;background-color: rgb(248 249 250)&quot; src=&quot;https://j0.lol/blog/webcomponentsforfunandprofit/trC8Pkkrjk-535.svg&quot; alt=&quot;a diagram demonstating the infrastructure of this website, compared to the previous&quot;&gt;
		&lt;figcaption&gt;The three types of Web component. &lt;br&gt;(CSR is client-side rendered, SSR is server-side rendered.)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Making Imperative Shadow DOM Components&lt;/h2&gt;

&lt;p&gt;You define a component in JavaScript by making a class and then adding your component to a registry. Let&#39;s try and recreate this element in a web component:&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;Hello There! This is a simple component that wraps around a provided element.&lt;/p&gt;
		&lt;/aside&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;js&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;class&lt;/a-k&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt; &lt;a-k&gt;extends&lt;/a-k&gt; &lt;a-cr&gt;HTMLElement&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
	&lt;a-f&gt;connectedCallback&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;shadow&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;this&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;attachShadow&lt;/a-f&gt;&lt;a-p&gt;({&lt;/a-p&gt; &lt;a-pr&gt;mode&lt;/a-pr&gt;: &lt;a-s&gt;&amp;quot;open&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;});&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;wrapper&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;div&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;display&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;flex&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;speechImage&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;img&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;src&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;https://j0.lol/static/speech/deer/neutral.png&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;setAttribute&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;height&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;120px&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;speechBoxBorder&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;div&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;border&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;5px solid rgb(176 152 232)&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;background&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;black&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;color&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;white&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;slot&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;slot&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;slot&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-v&gt;shadow&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;
	&lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-v&gt;customElements&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;define&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;speech-box&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt;&lt;a-p&gt;);&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
	&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;Hello there!&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;span style=&quot;display: block; margin-top: -0.8em; margin-bottom: 1em;&quot;&gt;&lt;small&gt;&lt;center&gt;&lt;a href=&quot;https://codepen.io/j0lol/pen/zxrpGqj&quot;&gt;Codepen sample&lt;/a&gt;&lt;/center&gt;&lt;/small&gt;&lt;/span&gt;

&lt;p&gt;
		When the component gets loaded, &lt;code&gt;connectedCallback()&lt;/code&gt; is called. This lets you do a lot, but here we instantiate a Shadow DOM &#39;root&#39; node, and imperatively add nodes to it. The &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element will let you poke a hole in your Shadow DOM to include Light DOM elements.
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-worried&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
		That &lt;code&gt;connectedCallback()&lt;/code&gt; is pretty ugly! Nobody wants to write HTML like this. Or CSS! Surely there&#39;s a nicer way to do this, right?
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;Yeah. I wanted to demonstrate taking &#39;imperative&#39; to it&#39;s logical conclusion. This is what libraries like &lt;a href=&quot;https://lit.dev&quot;&gt;Lit&lt;/a&gt; help with. Here&#39;s what a very simple component looks like in Lit:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;js&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;import&lt;/a-k&gt; &lt;a-p&gt;{&lt;/a-p&gt;&lt;a-v&gt;html&lt;/a-v&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;css&lt;/a-v&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-cr&gt;LitElement&lt;/a-cr&gt;&lt;a-p&gt;}&lt;/a-p&gt; &lt;a-k&gt;from&lt;/a-k&gt; &lt;a-s&gt;&amp;#39;lit&amp;#39;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

&lt;a-k&gt;export&lt;/a-k&gt; &lt;a-k&gt;class&lt;/a-k&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt; &lt;a-k&gt;extends&lt;/a-k&gt; &lt;a-cr&gt;LitElement&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
	&lt;a-k&gt;static&lt;/a-k&gt; &lt;a-pr&gt;styles&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-f&gt;css&lt;/a-f&gt;&lt;a-s&gt;`
		&lt;/a-s&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;border&lt;/a-pr&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-p&gt;{&lt;/a-p&gt;&lt;a-s&gt;
			&lt;/a-s&gt;&lt;a-pr&gt;border&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-n&gt;5&lt;/a-n&gt;&lt;a-t&gt;px&lt;/a-t&gt;&lt;a-s&gt; solid &lt;/a-s&gt;&lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-n&gt;176&lt;/a-n&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-n&gt;152&lt;/a-n&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-n&gt;232&lt;/a-n&gt;&lt;a-p&gt;);&lt;/a-p&gt;&lt;a-s&gt;
			&lt;/a-s&gt;&lt;a-pr&gt;background&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt;&lt;a-s&gt; black&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt;&lt;a-s&gt; white&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;&lt;a-s&gt;
		&lt;/a-s&gt;&lt;a-p&gt;}&lt;/a-p&gt;&lt;a-s&gt;`&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

	&lt;a-f&gt;render&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
		&lt;a-k&gt;return&lt;/a-k&gt; &lt;a-f&gt;html&lt;/a-f&gt;&lt;a-s&gt;`&lt;/a-s&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-s&gt; &lt;/a-s&gt;&lt;a-at&gt;class&lt;/a-at&gt;&lt;a-s&gt;=&amp;quot;&lt;/a-s&gt;&lt;a-s&gt;border&lt;/a-s&gt;&lt;a-s&gt;&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;&amp;gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;&lt;a-s&gt;`&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
	&lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-v&gt;customElements&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;define&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;#39;speech-box&amp;#39;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt;&lt;a-p&gt;);&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;span style=&quot;display: block; margin-top: -0.8em; margin-bottom: 1em;&quot;&gt;&lt;small&gt;&lt;center&gt;&lt;a href=&quot;https://lit.dev/playground/#sample=examples%2Fslotting-children&amp;project=W3sibmFtZSI6Im15LWVsZW1lbnQuanMiLCJjb250ZW50IjoiaW1wb3J0IHtodG1sLCBjc3MsIExpdEVsZW1lbnR9IGZyb20gJ2xpdCc7XG5cbmV4cG9ydCBjbGFzcyBTcGVlY2hCb3ggZXh0ZW5kcyBMaXRFbGVtZW50IHtcbiAgc3RhdGljIHN0eWxlcyA9IGNzc2BcbiAgICBkaXYuYm9yZGVyIHtcbiAgICAgIGJvcmRlcjogNXB4IHNvbGlkIHJnYigxNzYgMTUyIDIzMik7XG4gICAgICBiYWNrZ3JvdW5kOiBibGFjazsgY29sb3I6IHdoaXRlO1xuICAgIH1gO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICByZW5kZXIoKSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdiBjbGFzcz1cImJvcmRlclwiPjxzbG90Pjwvc2xvdD48L2Rpdj5gO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3NwZWVjaC1ib3gnLCBTcGVlY2hCb3gpOyJ9LHsibmFtZSI6ImluZGV4Lmh0bWwiLCJjb250ZW50IjoiPCFkb2N0eXBlIGh0bWw-XG48aHRtbD5cbjxoZWFkPlxuICA8c2NyaXB0IHR5cGU9XCJtb2R1bGVcIiBzcmM9XCIuL215LWVsZW1lbnQuanNcIj48L3NjcmlwdD5cbjwvaGVhZD5cbjxib2R5PlxuICA8c3BlZWNoLWJveD5cbiAgICA8cD5IZWxsbyBUaGVyZSE8L3A-XG4gIDwvc3BlZWNoLWJveD5cbjwvYm9keT5cbjwvaHRtbD5cbiJ9LHsibmFtZSI6InBhY2thZ2UuanNvbiIsImNvbnRlbnQiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcImxpdFwiOiBcIl4zLjAuMFwiLFxuICAgIFwiQGxpdC9yZWFjdGl2ZS1lbGVtZW50XCI6IFwiXjIuMC4wXCIsXG4gICAgXCJsaXQtZWxlbWVudFwiOiBcIl40LjAuMFwiLFxuICAgIFwibGl0LWh0bWxcIjogXCJeMy4wLjBcIlxuICB9XG59IiwiaGlkZGVuIjp0cnVlfV0&quot;&gt;Lit Playground&lt;/a&gt;&lt;/center&gt;&lt;/small&gt;&lt;/span&gt;

&lt;p&gt;Being able to declaratively define your CSS and HTML is a lot nicer, and a lot of people who try to go down this route just &lt;a href=&quot;https://bsky.app/profile/justinfagnani.com/post/3ltfade6sl22z&quot;&gt;re-invent Lit anyway&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Making Imperative &lt;em&gt;Light DOM&lt;/em&gt; Components&lt;/h2&gt;

&lt;p&gt;This classification is more of a &#39;trick&#39; more than anything. But it&#39;s a real thing you can do! You might prefer to work in the Light DOM if, for example, you are making components for your own website, and don&#39;t need encapsulation of styling. One thing to note is: because you lose the Shadow DOM, you lose the functionality of &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; (and its sister &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;), which means you need to manually move around things you want slotted. Not much of a loss, though!&lt;/p&gt;



		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;js&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;class&lt;/a-k&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt; &lt;a-k&gt;extends&lt;/a-k&gt; &lt;a-cr&gt;HTMLElement&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
	&lt;a-f&gt;connectedCallback&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
		&lt;a-c&gt;// get children before appending anything to element&lt;/a-c&gt;
		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;previousChildren&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;this&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;children&lt;/a-pr&gt;&lt;a-p&gt;;&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;wrapper&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;div&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;display&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;flex&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

		&lt;a-k&gt;const&lt;/a-k&gt; &lt;a-v&gt;speechImage&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;img&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;src&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;https://j0.lol/static/speech/deer/neutral.png&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;setAttribute&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;height&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;120px&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-k&gt;let&lt;/a-k&gt; &lt;a-v&gt;speechBoxBorder&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-v&gt;document&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;createElement&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;div&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;border&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;5px solid rgb(176 152 232)&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;background&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;black&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;style&lt;/a-pr&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;color&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;white&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;;&lt;/a-p&gt;

		&lt;a-c&gt;// slot children in here&lt;/a-c&gt;
		&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;replaceChildren&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;...&lt;a-v&gt;previousChildren&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;speechImage&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;
		&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;speechBoxBorder&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;

		&lt;a-v&gt;this&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;appendChild&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;wrapper&lt;/a-v&gt;&lt;a-p&gt;);&lt;/a-p&gt;
	&lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-v&gt;customElements&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;define&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;speech-box&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-cr&gt;SpeechBox&lt;/a-cr&gt;&lt;a-p&gt;);&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;span style=&quot;display: block; margin-top: -0.8em; margin-bottom: 1em;&quot;&gt;&lt;small&gt;&lt;center&gt;&lt;a href=&quot;https://codepen.io/j0lol/pen/xbZpGoo&quot;&gt;Codepen sample&lt;/a&gt;&lt;/center&gt;&lt;/small&gt;&lt;/span&gt;


&lt;p&gt;Please note that you don&#39;t have to do this, and a simple custom element without any Javascript like this could still be called an &quot;(Imperative) Light DOM&quot; component.&lt;/p&gt;
&lt;h2&gt;Making Declarative Shadow DOM Components&lt;/h2&gt;

&lt;p&gt;So &quot;Declarative Shadow DOM&quot; is a method of using &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; (with a &lt;code&gt;shadowrootmode&lt;/code&gt; attribute) to define a shadow DOM root in HTML only. You still need Javascript for initializing your custom element, and adding interactivity, but you can define styles and layout in HTML. You can make one like this:


		&lt;/p&gt;&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
	&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt; &lt;a-at&gt;shadowrootmode&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;open&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
		&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt; &lt;a-at&gt;style&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;display: flex;&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
			&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;img&lt;/a-tg&gt; &lt;a-at&gt;src&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;https://j0.lol/static/speech/deer/neutral.png&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;height&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;120px&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
			&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt; &lt;a-at&gt;style&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;border: 5px solid rgb(176, 152, 232); background: black; color: white;&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
				&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;slot&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
			&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
		&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;div&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
	&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;

	&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;Hello there!&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
&lt;span style=&quot;display: block; margin-top: -0.8em; margin-bottom: 1em;&quot;&gt;&lt;small&gt;&lt;center&gt;&lt;a href=&quot;https://codepen.io/j0lol/pen/dPGJYNX&quot;&gt;Codepen sample&lt;/a&gt;&lt;/center&gt;&lt;/small&gt;&lt;/span&gt;

&lt;p&gt;
		Compared to imperative examples, this is a lot simpler to understand. You make a template, punch a &quot;slot&quot; hole in it, and it just works. I like the idea of this a lot. You can just spit this out with PHP, or whatever you use as your server backend. The issue is, every time you want to use one of these components, you need to include the template &lt;em&gt;again&lt;/em&gt;. This makes it quite a pain to use in practice, unless you want to do something like this:
&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;php&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&amp;lt;speech-box&amp;gt;
	&lt;a-tg&gt;&amp;lt;?php&lt;/a-tg&gt; &lt;a-k&gt;echo&lt;/a-k&gt; &lt;a-f&gt;speechBoxTemplate&lt;/a-f&gt;(); &lt;a-tg&gt;?&amp;gt;&lt;/a-tg&gt;

	&amp;lt;p&amp;gt;Hello there!&amp;lt;/p&amp;gt;
&amp;lt;/speech-box&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
&lt;span style=&quot;display: block; margin-top: -0.8em; margin-bottom: 1em;&quot;&gt;&lt;center&gt;&lt;small&gt;(Did you know that you can use &amp;lt;?= and ?&amp;gt; here instead to omit the echo?)&lt;/small&gt;&lt;/center&gt;&lt;/span&gt;


&lt;p&gt;You might think:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Why didn&#39;t they just let you name and reuse &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; blocks, so you don&#39;t have to repeat yourself constantly?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;It turns out that the people who proposed this &lt;a href=&quot;https://github.com/mfreed7/declarative-shadow-dom?tab=readme-ov-file#isnt-it-tedious-to-repeat-the-contents-of-each-shadow-root&quot;&gt;had thought of this&lt;/a&gt;. Their answer is that you can just copy &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; blocks with Javascript to repeat blocks of HTML. That&#39;s nice, but it does make this use case a bit more painful. Of course, something like this can easily be abstracted over with &lt;a href=&quot;https://lit.dev/docs/ssr/overview/&quot;&gt;a framework&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;
There are a few ways that this could be improved in the future. One proposal, &lt;a href=&quot;https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Declarative-Custom-Elements-Strawman.md&quot;&gt;&lt;em&gt;Declarative Custom Elements&lt;/em&gt;&lt;/a&gt;, would allow you to define an entire element in HTML so you could use it later without being encumbered with the template.
&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;definition&lt;/a-tg&gt; &lt;a-at&gt;name&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;my-element&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;constructor&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;MyElement&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
		&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt; &lt;a-at&gt;shadowmode&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;closed&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt; ~ &lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;template&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
		&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;script&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
			&lt;a-k&gt;class&lt;/a-k&gt; &lt;a-cr&gt;MyElement&lt;/a-cr&gt; &lt;a-k&gt;extends&lt;/a-k&gt; &lt;a-cr&gt;HTMLElement&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-o&gt;~&lt;/a-o&gt; &lt;a-p&gt;}&lt;/a-p&gt;
		&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;script&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;definition&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;



&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;
I hope this was an okay introduction to Web Components! If you are just starting out using them, I think it&#39;s important to know &lt;em&gt;what you are doing&lt;/em&gt; with them. Mainly just so you can pick the best method of using them. If you think I didn&#39;t give a good overview (or just want more to read), please read through some of the provided articles below. Thanks!
&lt;/p&gt;
&lt;h3&gt;Further Reading&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components&quot;&gt;Web Components&lt;/a&gt; (MDN)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/web-components-are-easier-than-you-think/&quot;&gt;Web Components Are Easier Than You Think
&lt;/a&gt; (CSS-Tricks)&lt;/li&gt;

&lt;li&gt;Imperative and Declarative Shadow DOM
		&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2025/07/web-components-working-with-shadow-dom/&quot;&gt;Web Components: Working With Shadow DOM&lt;/a&gt; (Smashing Magazine)&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/declarative-shadow-dom&quot;&gt;Declarative Shadow Dom&lt;/a&gt; (Web.dev, Google Chrome team)&lt;/li&gt;
		&lt;/ul&gt;

&lt;/li&gt;
&lt;li&gt;Imperative Light DOM Web Components
		&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/blog/light-dom-only/&quot;&gt;Light-DOM-Only Web Components are Sweet&lt;/a&gt; (Frontend Masters)&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;https://chromamine.com/2024/10/you-can-use-web-components-without-the-shadow-dom/&quot;&gt; You can use Web Components without the shadow DOM&lt;/a&gt; (Harris Lapiroff)&lt;/li&gt;
		&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Miscellaneous
		&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/creating-custom-form-controls-with-elementinternals/&quot;&gt;Creating Custom Form Controls with ElementInternals
&lt;/a&gt; (CSS-Tricks)&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/styling-a-web-component/&quot;&gt;Styling a Web Component&lt;/a&gt; (CSS-Tricks)&lt;/li&gt;

		&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Website Version 5</title>
    <link href="https://j0.lol/blog/rewrite-v5/" />
    <updated>2025-10-16T19:50:00Z</updated>
    <id>https://j0.lol/blog/rewrite-v5/</id>
    <content type="html">&lt;p&gt;
    I rewrote my website again!
&lt;/p&gt;

&lt;p&gt; &lt;b&gt;TLDR&lt;/b&gt;&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;PHP &amp;rarr; Rust&lt;/li&gt;
    &lt;li&gt;Static HTML templating &amp;rarr; HTML inside Sqlite&lt;/li&gt;
    &lt;li&gt;Writing posts in-repo &amp;rarr; Web post creator&lt;/li&gt;
    &lt;li&gt;GitHub &amp;rarr; Tangled&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt; Whyyyyy? &lt;/h2&gt;

&lt;p&gt; &lt;s&gt;I&#39;m unemployed.&lt;/s&gt; I really hated the previous posting model of having to commit HTML to a git repo, commiting it, pushing it, git pulling on the other end, etc. With this new site, I can edit posts directly into a Sqlite DB.&lt;/p&gt;

&lt;h2&gt; The Stack&lt;/h2&gt;

&lt;p&gt;
    Umm. I&#39;m using Rust now. To &quot;simplify&quot; this, I will present this simple diagram.
&lt;/p&gt;

&lt;figure&gt;
    &lt;img width=&quot;1306&quot; height=&quot;646&quot; src=&quot;https://j0.lol/blog/rewrite-v5/RmGuDsCvl4-1306.svg&quot; alt=&quot;a diagram demonstating the infrastructure of this website, compared to the previous&quot;&gt;
    &lt;figcaption&gt;Version 4 (the previous website version) has a much simpler infrastructure. I wish Rust was this simple, but it comes with the territory.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
    I could have probably done this all in PHP. But it would have been a lot messier.
&lt;/p&gt;

&lt;h3&gt;The &lt;em&gt;&quot;Templating Engine&quot;&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;I like &lt;a href=&quot;https://maud.lambda.xyz/&quot;&gt;&lt;code&gt;maud&lt;/code&gt;&lt;/a&gt; a lot. I&#39;ve been eyeing it up for a future project. Let me explain the appeal.&lt;/p&gt;

&lt;p&gt; Most Rust templating languages (&lt;a href=&quot;https://docs.rs/minijinja/latest/minijinja/&quot;&gt;&lt;code&gt;minijinja&lt;/code&gt;&lt;/a&gt;) look like this:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;{&amp;amp;percnt; for user in users &amp;amp;percnt;}
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;li&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;&amp;amp;lcub;&amp;amp;lcub; user.name &amp;amp;rcub;&amp;amp;rcub;&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;li&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
{&amp;amp;percnt; endfor &amp;amp;percnt;}
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt; You have to specify this in a separate file, and serialise any types you use. It&#39;s kind of annoying and necessitates something like &lt;code&gt;serde&lt;/code&gt; to do wizard magic to your &lt;code&gt;struct&lt;/code&gt;s and &lt;code&gt;enum&lt;/code&gt;s. &lt;code&gt;Maud&lt;/code&gt; looks like this:&lt;/p&gt;



		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-m&gt;html!&lt;/a-m&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    h1 &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-s&gt;&amp;quot;Hello, world!&amp;quot;&lt;/a-s&gt; &lt;a-p&gt;}&lt;/a-p&gt;
    p&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;intro&lt;/a-pr&gt;&lt;a-p&gt; &lt;/a-p&gt;&lt;a-p&gt;{&lt;/a-p&gt;&lt;a-p&gt;
        &lt;/a-p&gt;&lt;a-s&gt;&amp;quot;This is an example of the &amp;quot;&lt;/a-s&gt;&lt;a-p&gt;
        a href=&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;https://github.com/lambda-fairy/maud&amp;quot;&lt;/a-s&gt;&lt;a-p&gt; &lt;/a-p&gt;&lt;a-p&gt;{&lt;/a-p&gt;&lt;a-p&gt; &lt;/a-p&gt;&lt;a-s&gt;&amp;quot;Maud&amp;quot;&lt;/a-s&gt;&lt;a-p&gt; &lt;/a-p&gt;&lt;a-p&gt;}&lt;/a-p&gt;&lt;a-p&gt;
        &lt;/a-p&gt;&lt;a-s&gt;&amp;quot; template language.&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;
    &lt;/a-p&gt;&lt;a-p&gt;}&lt;/a-p&gt;&lt;a-p&gt;
&lt;/a-p&gt;&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt; You really can just put this as the return type of a function. It&#39;s so much nicer. I don&#39;t have to fumble with a bunch of named &lt;code&gt;.html.jinja&lt;/code&gt; files and all of your templating is &quot;just there&quot; with the code. It&#39;s honestly what I love about PHP so much. As a plus, you can just put &lt;em&gt;any expression&lt;/em&gt; and splice it into your templates. Yes, you can nest this. It&#39;s ugly.
&lt;/p&gt;

&lt;h3&gt;Ok, but why do you have a HTML rewriter?&lt;/h3&gt;

&lt;p&gt;Uhhmm. Well so I made a web editor for creating new posts.&lt;/p&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/rewrite-v5/i3g6cDpfn8-1285.webp 1285w&quot;&gt;&lt;img width=&quot;1285&quot; height=&quot;850&quot; style=&quot;background-color: white&quot; src=&quot;https://j0.lol/blog/rewrite-v5/i3g6cDpfn8-1285.jpeg&quot; alt=&quot;a screenshot of the post creator of my website. there are two panels, with an editor and a preview.&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;It&#39;s a bit shabby.&lt;/figcaption&gt;
&lt;/figure&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
    So, this necessitated a move from the previous way I was doing the &#39;speech-box&#39; element &lt;b&gt;(this thing!)&lt;/b&gt;. Previously, I was using PHP to template-in the begin and end text around it, with a function to alternate between pictures.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;I wanted to add a custom element. On web, this is &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components&quot;&gt;pretty simple&lt;/a&gt;. On the server, however, I am just feeding HTML into &lt;code&gt;maud&lt;/code&gt;. So, I wrote a rewrite rule in &lt;code&gt;lol-html&lt;/code&gt;:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;let&lt;/a-k&gt; contents = &lt;a-f&gt;rewrite_str&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;post&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;contents&lt;/a-pr&gt;&lt;a-p&gt;,&lt;/a-p&gt;
    &lt;a-t&gt;RewriteStrSettings&lt;/a-t&gt; &lt;a-p&gt;{&lt;/a-p&gt;
        &lt;a-pr&gt;element_content_handlers&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-m&gt;vec!&lt;/a-m&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-m&gt;element!&lt;/a-m&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;speech-box&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; |el| &lt;a-p&gt;{&lt;/a-p&gt;
            &lt;a-k&gt;let&lt;/a-k&gt; &lt;a-t&gt;char&lt;/a-t&gt; = el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get_attribute&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;character&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap_or&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;deer&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;to_string&lt;/a-f&gt;&lt;a-p&gt;());&lt;/a-p&gt;
            &lt;a-k&gt;let&lt;/a-k&gt; emotion = el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;get_attribute&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;emotion&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap_or&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;neutral&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;to_string&lt;/a-f&gt;&lt;a-p&gt;());&lt;/a-p&gt;

            &lt;a-k&gt;let&lt;/a-k&gt; &lt;a-cr&gt;SpeechDetails&lt;/a-cr&gt; &lt;a-p&gt;{&lt;/a-p&gt; class&lt;a-p&gt;,&lt;/a-p&gt; alt&lt;a-p&gt;,&lt;/a-p&gt; src &lt;a-p&gt;}&lt;/a-p&gt; = &lt;a-f&gt;render_speech&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-c&gt;/* snip who cares */&lt;/a-c&gt;&lt;a-p&gt;);&lt;/a-p&gt;

            el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;set_tag_name&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;div&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;?&lt;a-p&gt;;&lt;/a-p&gt;
            el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;set_attribute&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;class&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-m&gt;format!&lt;/a-m&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;dialog speech {class}&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;))&lt;/a-p&gt;?&lt;a-p&gt;;&lt;/a-p&gt;
            el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;before&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-o&gt;&amp;amp;&lt;/a-o&gt;&lt;a-m&gt;format!&lt;/a-m&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;r#&amp;quot;&amp;lt;div class=&amp;quot;dialog-box&amp;quot;&amp;gt; &amp;lt;img class=&amp;quot;raw dialog profile&amp;quot; width=&amp;quot;120&amp;quot; height=&amp;quot;120&amp;quot; src=&amp;quot;{src}&amp;quot; alt=&amp;quot;{alt}&amp;quot;&amp;gt; &amp;quot;#&lt;/a-s&gt;&lt;a-p&gt;),&lt;/a-p&gt; &lt;a-t&gt;ContentType&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Html&lt;/a-cr&gt;&lt;a-p&gt;);&lt;/a-p&gt;
            el&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;after&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-t&gt;ContentType&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-cr&gt;Html&lt;/a-cr&gt;&lt;a-p&gt;);&lt;/a-p&gt;

            &lt;a-f&gt;Ok&lt;/a-f&gt;&lt;a-p&gt;(())&lt;/a-p&gt;
        &lt;a-p&gt;})],&lt;/a-p&gt;
        ..&lt;a-t&gt;RewriteStrSettings&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;new&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;).&lt;/a-p&gt;&lt;a-f&gt;unwrap&lt;/a-f&gt;&lt;a-p&gt;();&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;It&#39;s somewhat gross, but it means I don&#39;t have to run JS just to put funny images on the screen. I don&#39;t want to deal with &lt;a href=&quot;https://web.dev/articles/optimize-cls&quot;&gt;layout shift&lt;/a&gt; and &lt;a href=&quot;https://gomakethings.com/web-components-and-fouc/&quot;&gt;FOUC&lt;/a&gt; and all the other annoying client rendering issues.&lt;/p&gt;

&lt;p&gt; Now I can just do this and not worry about it: &lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt; &lt;a-at&gt;character&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;deer&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;expression&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;happy&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;awawawawawawawawawa&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;speech-box&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;h3&gt;The Web Framework&lt;/h3&gt;

I have so little to say about &lt;a href=&quot;https://docs.rs/poem/latest/poem/&quot;&gt;&lt;code&gt;poem&lt;/code&gt;&lt;/a&gt;. Which is really awesome! If you have used &lt;a href=&quot;https://docs.rs/axum/latest/axum/&quot;&gt;&lt;code&gt;axum&lt;/code&gt;&lt;/a&gt; before, it&#39;s basically just that but with everything you want bundled inside. No more &lt;a href=&quot;https://docs.rs/tower-sessions/latest/tower_sessions/&quot;&gt;&lt;code&gt;tower_sessions&lt;/code&gt;&lt;/a&gt;!! It&#39;s nice. I like it.

&lt;h2&gt;Moving to Tangled, and other thoughts&lt;/h2&gt;

&lt;p&gt;Unsure what to think about &#39;git forges&#39; in general. Ideally, there would be a perfect code forge that:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;hosts all of you stuff for you, for free, forever&lt;/li&gt;
    &lt;li&gt;isn&#39;t hosted by a for-profit corporation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This doesn&#39;t really exist, but &lt;a href=&quot;https://tangled.org&quot;&gt;Tangled&lt;/a&gt; is a step towards this. It&#39;s built on ATProto (which is decentralized), therefore you can log in with your Bluesky account. You can host your own code storage (so if Tangled Corp Inc&amp;trade; annoys you personally, you can move your data away from them) but I don&#39;t care about that too much. I like how the frontend feels much lighter than GitHub. I probably won&#39;t move all of my projects to this though (yet?)&lt;/p&gt;

&lt;h3&gt;Using &lt;code&gt;jj&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;
    I decided to try &lt;a href=&quot;https://jj-vcs.github.io/jj/latest/&quot;&gt;Jujutsu&lt;/a&gt; for this project. I like it a lot! It&#39;s a version control system that &quot;wraps over Git&quot; and lets you control the underlying data in a different way. My favorite feature is that you can write what you do &lt;em&gt;before&lt;/em&gt; you do it, instead of trying to name what you have done after the fact. It&#39;s mostly a psychological thing, but I feel like this helps me stay focused on what I set out to do.
&lt;/p&gt;
&lt;p&gt;I would suggest trying it if this seems interesting to you, or if Git confuses you a lot.&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;That&#39;s all I have to say for now. I did a &lt;a href=&quot;https://tangled.org/@j0.lol/bog/commits/trunk&quot;&gt;decent amount of work&lt;/a&gt;, and theres a lot of stuff I didn&#39;t bother mentioning. (Check out the new footer!) Feel free to look around my code. It&#39;s a little bit messy right now, but it&#39;s not mission-critical code. Feel free to steal it for your own website if you want, I don&#39;t care. (Just respect the &lt;a href=&quot;https://tangled.org/@j0.lol/bog/blob/trunk/LICENSE-MIT&quot;&gt;LICENSE&lt;/a&gt;!)&lt;/p&gt;
		&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>On loving your website</title>
    <link href="https://j0.lol/blog/love-your-website/" />
    <updated>2025-06-27T15:00:00Z</updated>
    <id>https://j0.lol/blog/love-your-website/</id>
    <content type="html">&lt;p&gt;
    I think — at least for me — that blogging is a form of journaling. I feel
    like if I was journaling physically&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_physicalnotetaking&quot;&gt;[1]&lt;/a&gt; I would
    love to take care of a physical &lt;em&gt;tome&lt;/em&gt; containing my thoughts.
    Decorate it, take care of it, et cetera. Following this logic, I take great
    joy in taking care of my website. Polishing the CSS, adding nice features,
    making a nice favicon&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_favicon&quot;&gt;[2]&lt;/a&gt;, and other stuff (sorry I have
    been working on it a lot recently.) So, uh, I think I want to just gush
    about all the changes I have made recently to my site, and maybe circle back
    to some deeper point about self-love.
&lt;/p&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/2JslAZqNx6-1302.webp 1302w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1302&quot; height=&quot;1310&quot; alt=&quot;Lots of commits.&quot; src=&quot;https://j0.lol/blog/love-your-website/2JslAZqNx6-1302.jpeg&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;Lots of commits.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Favicons are weird&lt;/h2&gt;

&lt;p&gt;I have many questions about favicons.&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;
        Why are they called favicons?&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_whyfavicon&quot;&gt;[3]&lt;/a&gt; Favorite icons?
        Shouldn&#39;t they be called like bookmark&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_bookmarksaside&quot;&gt;[4]&lt;/a&gt; icons
        or something? Did they make this up in like Netscape Navigator or
        Internet Explorer or something?&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_ie&quot;&gt;[5]&lt;/a&gt;
    &lt;/li&gt;
    &lt;li&gt;
        Why are they so small? It&#39;s really hard to represent anything in a
        16&amp;times;16 square. At least we can now use 32&amp;times;32, and people who
        know their way around a vector design program can use an SVG.
    &lt;/li&gt;
    &lt;li&gt;
        How have we not come up with a better way of linking an icon to your
        website than stuffing a bunch of
        &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;&lt;a href=&quot;https://j0.lol/blog/love-your-website/googwebmanifest&quot;&gt;[6]&lt;/a&gt;??
    &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
    Um. Sorry about all the footnotes. When I started working on my current
    iteration of my site, which I arbitrarily named V4, I was getting into
    drawing at the same time. I wasn&#39;t sure what to fit in that thing, so I just
    put the drawing I had in there. It uh. It looked bad. Here&#39;s a recreation:
    &lt;img style=&quot;display: inline;&quot; loading=&quot;lazy&quot; class=&quot;raw&quot; src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAEKADAAQAAAABAAAAEAAAAADHbxzxAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgoZXuEHAAACs0lEQVQ4EaWTXW8bRRSGn5nd9cf6Mx/YaWMcYzdpSgMSVXvRilvEBT8B/h//AVVcVOICVUXqh8BpWkibOFBqU3d3vbbX3h3OOAqEa87srvbMzPvOOzPvUd3ObcP/CPcyVkmileaCMTMZSppW6j99lzH/ENiJqQCCdIK2MGEra5+lWRKm8xUR8i05+ct4VgQX4Eouz43yDot0yXKpeBYe0fDW2at2MU4CRvHi/Yks4KxIjOjSFmyZI95Ry1q8eeVz/JuL79S4vfExV0rrRBPFr4ce7/+osuZsMidmYdKVKrWzc8s4OqXjfkqrHbLfe0s8c3jwk8/irCWLGm7eiuh9dEqaNHn4JMePv7/kw0qFs9kQ18qupW163YQv7ymm6YwNGdxvO4zeBeAEbNYXK5Wb1ZjWVsTrbxtcKa5T89bQe9U9zKTEzd0Yz/Fp1DPORiF/TU4Zxs9EwRlaj4RkxuvhgOnM46u7dU4HKQkT3EF8Qj7XIY426Q/6hLEjk7WQyTYeF/j8wDCdG34+VmxUS1QKOd78WWScO2IUj9BH0YDEH3D/hyVatrLdiCjmFPcfKb75wtDdhuHYodUMKOVrPDqc8Ph5wP4HTaKFHKR14l5ll+fhIT3uoKpDvjt5CgLKN1zmsjrjGfdudClkG3x//Ire9oTrpQN+Cfu4qVxH2StTcauc8JDPcnf5eneL0fwtwTzC1ZrmtSYFXeYo6NO+OqbrH1D0isyzBepq+xPTK3VoFrdEUkSSJoSLEN/zBVSU0zcEi4BlllLP1ym6BTGSph8cyo1NUb3OHTPN5rhSAwVdYJbNxL7pytYp2cpxnnJXdrN1UlB5xmlISchd5eBaO/oCzKQlWYKnPHLyWncqWxASRmrE5laNULPmVsiMRZrzWrBgO+wI43m3JPJnn3/jPLHybdFdxN8uVB7pghou7AAAAABJRU5ErkJggg==&quot; width=&quot;16&quot; height=&quot;16&quot;&gt;
    . Ew, right? So a few months ago I made this new drawing, the dialog box
    deer that you have definitely seen on this site already. And I realised that
    I can adapt it into a favicon:
    &lt;img style=&quot;display: inline;&quot; loading=&quot;lazy&quot; class=&quot;raw&quot; src=&quot;data:image/x-icon;base64,AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAOgkAADoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////ABYPEQgWDxHJFg8R5hYPEa8WDxGwFg8RiBYPEQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8RCRYPEUwWDxF/Fg8RkxYPEX0WDxE9Fg8RAv///wAAAAAARJO2C0ys1uZOsdz/QYur/0GLrP8+hKPRVMPyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////ABYPEUIZFxrWKUdW9zhxjOk9f53sN2+J6yU7SPkXERPOFg8RSgAAAABUw/IDUr7rjVTD8v9Uw/L/VMPy/1PB8J8AAAAA////AP///wAWDxEXFg8RTxYPEXMWDxFzFg8RYxYPESwWDxEC////AP///wD///8AAAAAAAAAAAAWDxFNJz9O9U2w2u5UwvH0VMPy01TD8tJUw/LYU8Hw+Uyr1O4oRFP9Fg8RjhYPEQRUw/IJVMPyslTD8shUw/JnVMPyBQAAAAAWDxEDFg8RbBwhJ+wqSlvyNWqD4zZsheAvWW3yIS859BcRE8oWDxFeFg8RAQAAAAAAAAAAFg8RKilHV/dSvuz4VMPymFTD8h4AAAAAAAAAAFTD8gNUw/IyVMPyplTC8f05dI/9GBUYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcTFn8xXHH8Ur3q81TC8e1Uw/LMVMPyxlTC8etTwfD0TK3X5i5UZ/YWEBKeFg8RBQAAAAAlOkevUr3q/FPB8HoWDxE+Fg8RoRYPEQL///8AAAAAABYPET4WDxFbVMPyXlTC8P00Zn35Fg8RFAAAAAD///8A////AP///wAWDxERNmuE9VPB8PtUw/JrVMPyEgAAAAAAAAAAVMPyDFTD8jFUw/KFVMLw8j2CoPgYFhmFFg8RBD+GpfFOs97YVMPyAjNkfIM9gqD9GxwiGwAAAAAWDxEZJz9N5zJheMcAAAAATKzWkVG76P8mPkxMAAAAAAAAAAAAAAAAAAAAAC9XbCpQtuL9SqfPdxYPESMWDxGsFg8REf///wD///8A////ABYPESgsUGJbUr3q9jhxjPA1aoIITK3W+j2CoIcAAAAASKDGcVO/7f8hLjiSFg8RRx0kLNNNrtf9ULfktAAAAAAvWGxZUr7s/z2AnmIAAAAAAAAAAAAAAAAAAAAAN26IM0+03/03b4k6NGZ9SUCIqf8gLDVrAAAAAAAAAAAAAAAAJj1LnCc/TfpAiqq5Ta7Y/zVqgghMrdb6OniUfwAAAABUw/IiU8Hv/TdvifsnQVD/SqbO9VPA7vBUw/IiFg8RBShFVNBTwe//Qo+xQxYPEQIAAAAAAAAAAAAAAABHncMiT7Xg/CxPYVdTwO4tVMPy/y5WafQWDxFCFg8RIRYPEXw4co36U8Du/Dx+m79UwvD/VMPyBE6w2/QxX3SqAAAAAAAAAABUw/KGVMLx91TD8v9Uw/HLVMPyOBYPERsYFBisRZe7/0ys1f4cISjtFxET2xYPEcMWDxGkFg8RpBkYHKZNr9n+LVJlrFTD8gFRu+e3Ubrm+yY+S/8eJy78NGd//lO/7fpLqdKcN3CK/E6z3t8WDxFaS6nR0zhyjfMWDxEuAAAAAAAAAABUw/IiVMPyLBgWGhoWDxF7HSQr7ECKq/hUw/L/U8Hw+VK96vNPtN/vSqXM6kGNrutBja7rQYyu6lK96/w7epfyFg8RF1TD8htUwvDIVMPy/1TC8f1Uw/L9S6nRjyIzPupTwO79UbrnaS1RY/cxXnTsTrHb/yIyPO8WDxGLFg8RZBYPEawWDxHPHCIo8jVpgvZRu+j2U8Hw7FTD8kdUw/I6VMPya1TD8otUw/KlVMPyxlTD8sZTv+3KSaPK3E6x2/ofKDGjAAAAAFTD8gZUw/JEUbrmUCpJWnsgLTbxT7bh+FO/7b1Uw/IJU8Hw9E+14PJTwe/+ULbi/ThzjvsvWG32Q5Cz70uq0/BSvuz3U8Hw71TD8pFUw/IbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmkyxY8f5zYSqXM/zt8mf8WDxF8Fg8RaBYPEZQYFBfkL1Zq+lK86vdTv+67VMPyDgAAAABUw/IoVMPylVTD8sZTwfDqVMPy91TC8fFUw/LPVMPyr1TD8m5Uw/IcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVMPyBFO/7bFUw/L+VMLx/zRnfv8vWG3/O3uY91C45PJUwvH4VMPyjVTD8g0AAAAAAAAAAAAAAAAAAAAAAAAAAFTD8hFUw/IWVMPyEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVMPyC1TD8nJUw/LsVMPy/1TD8v9Uw/LtVMPyi1TD8isAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////////+A//8BgP/+AIHAfAABABgwPwAIGB4MABEeBwEBHgcBAA4AAYAAAADAAAAAAAAQAAD+ABAD/gA+P/8A////////////////////////////////////////////////8=&quot; width=&quot;32&quot; height=&quot;32&quot;&gt;. Much clearer, right? Just bicubic scaling the image down makes it quite
    readable, which I was honestly shocked at.
&lt;/p&gt;

&lt;h2 id=&quot;fonts&quot;&gt;Changing fonts&lt;/h2&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/2jUQVDSqBQ-1448.webp 1448w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;288&quot; src=&quot;https://j0.lol/blog/love-your-website/2jUQVDSqBQ-1448.jpeg&quot; alt=&quot;Screenshot of my blog, with an old rounded monospace heading font.&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;My old heading font&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/KQVN_ypvbC-1452.webp 1452w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1452&quot; height=&quot;192&quot; src=&quot;https://j0.lol/blog/love-your-website/KQVN_ypvbC-1452.jpeg&quot; alt=&quot;Screenshot of my blog, with an new rounded serif heading font.&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;My new heading font&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
    I&#39;m a programmer first, not a writer. So when I was making my website, it
    just made sense to add a touch of monospace to it. I imported my terminal
    font &lt;em style=&quot;font-family: var(--font-mono)&quot;&gt;(Maple Mono)&lt;/em&gt; into my
    project and set it as a heading font, italicized with cursive symbols.
    Though I was getting kind of tired of it. I don&#39;t think monospace fonts look
    that good when at such a large size. So I found this new font on Google
    Fonts (where else?) called
    &lt;em style=&quot;font-family: var(--font-serif)&quot;&gt;Hepta Slab&lt;/em&gt; and fell in love
    with it immediately. It&#39;s still very round, which captures the sort of look
    the rest of my fonts have (the sans-serif font is &lt;em&gt;DM Sans&lt;/em&gt;) while
    feeling way more in place than a monospaced font.
&lt;/p&gt;

&lt;p&gt;
    I think people all have complex relationship with fonts. Though maybe that&#39;s
    just my autism speaking. People hate
    &lt;em style=&quot;font-family: &amp;quot;Comic Sans MS&amp;quot;&quot;&gt;Comic Sans&lt;/em&gt;. People
    love to write Word documents with
    &lt;em style=&quot;font-family: Arial, Helvetica, sans-serif&quot;&gt;Arial&lt;/em&gt;. Web
    developers love &lt;em style=&quot;font-family: Inter, sans-serif&quot;&gt;Inter&lt;/em&gt;.
    Everyone sort of has their own font, in their
    &lt;span class=&quot;shantell&quot;&gt;handwriting&lt;/span&gt;&lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_shantell&quot;&gt;[7]&lt;/a&gt;. You might
    feel comforted by a &lt;span class=&quot;monospace&quot;&gt;monospace&lt;/span&gt; font.
    &lt;span class=&quot;cursive&quot;&gt;Cursive&lt;/span&gt;
    might remind you of rigid school structures.
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-happy&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
In other words, fonts are highly personal.
&lt;/p&gt;
		&lt;/aside&gt;

		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-shocked&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;

    Where did I get the idea to use
    &lt;span style=&quot;font-family: var(--font-serif)&quot;&gt;serif&lt;/span&gt; for headings?
    Honestly I&#39;m not sure. I&#39;ve seen it in a few places, like
    &lt;a href=&quot;https://highlysuspect.agency/&quot;&gt;Quat&#39;s blog&lt;/a&gt; and
    &lt;a href=&quot;https://zed.dev/docs/&quot;&gt;the Zed website&lt;/a&gt;.
    &lt;a href=&quot;https://www.smashingmagazine.com/2010/11/best-practices-of-combining-typefaces/&quot;&gt;Smashing Magazine&lt;/a&gt;
    has writing on this from 2010(!) so it&#39;s clearly not a new practice.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;h2&gt;Physicality&lt;/h2&gt;

&lt;div class=&quot;hstack&quot;&gt;
    &lt;figure&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/mijKscXHP0-1064.webp 1064w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;1298&quot; src=&quot;https://j0.lol/blog/love-your-website/mijKscXHP0-1064.jpeg&quot; alt=&quot;Screenshot of my website&#39;s home page. The &#39;page&#39; is very bare.&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;The home page is floating on a background.&lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure style=&quot; justify-content: space-between&quot;&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/gnPIhikWkb-1064.webp 1064w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1064&quot; height=&quot;1110&quot; src=&quot;https://j0.lol/blog/love-your-website/gnPIhikWkb-1064.jpeg&quot; alt=&quot;Screenshot of my website&#39;s home page. The &#39;page&#39; feels a lot more grounded, with borders and a shadow.&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;The page has a nice border and a shadow now.&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;
    As someone with autism, I have a divine fixation with tactile things. Shapes
    feel nice. During this redesign, I&#39;ve been trying to add physicality
    wherever possible. This isn&#39;t &lt;em&gt;just&lt;/em&gt; because of Apple&#39;s
    &lt;em&gt;Liquid Glass&lt;/em&gt; design language, but I think flat design is boring
    generally. I think many people do??
&lt;/p&gt;

&lt;p&gt;
    I&#39;ve been adding physicality wherever possible. The figures now have a
    shadow and border. Well, I haven&#39;t really been able to add it anywhere else,
    due to some rigid CSS limitations. I&#39;ll figure it out eventually. I hope
    this looks good!
&lt;/p&gt;

&lt;h2&gt;Cute little icons&lt;/h2&gt;

&lt;div class=&quot;hstack&quot;&gt;
    &lt;figure style=&quot;&quot;&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/lduOjlD5nN-770.webp 770w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;244&quot; src=&quot;https://j0.lol/blog/love-your-website/lduOjlD5nN-770.jpeg&quot; alt=&quot;Screenshot of old post index. Arrows are used for date.&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;Old arrow icons.&lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure style=&quot;&quot;&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/V27FZAUDpd-744.webp 744w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;226&quot; src=&quot;https://j0.lol/blog/love-your-website/V27FZAUDpd-744.jpeg&quot; alt=&quot;Screenshot of old post index. Clock icons are used for date.&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;New, nice clock icons.&lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;

&lt;div class=&quot;hstack&quot;&gt;
    &lt;figure style=&quot; justify-content: space-between&quot;&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/tvH8UudJa7-1386.webp 1386w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1386&quot; height=&quot;966&quot; src=&quot;https://j0.lol/blog/love-your-website/tvH8UudJa7-1386.jpeg&quot; alt=&quot;Screenshot of old comment section&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;
            Old comment section. Uses text for reply and like labels.
        &lt;/figcaption&gt;
    &lt;/figure&gt;

    &lt;figure style=&quot;&quot;&gt;
        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/love-your-website/VXTONsWjwK-1412.webp 1412w&quot;&gt;&lt;img loading=&quot;lazy&quot; width=&quot;1412&quot; height=&quot;988&quot; src=&quot;https://j0.lol/blog/love-your-website/VXTONsWjwK-1412.jpeg&quot; alt=&quot;Screenshot of new comment section&quot;&gt;&lt;/picture&gt;
        &lt;figcaption&gt;
            New comment section. Lots of icons, and a &lt;em&gt;LOT&lt;/em&gt; of tweaking
            to get things feeling better.*
        &lt;/figcaption&gt;
    &lt;/figure&gt;
&lt;/div&gt;
&lt;span&gt;&lt;small&gt;＊ Note that the comment lines are very thin, and you might have to
        zoom in to see them.&lt;/small&gt;&lt;/span&gt;
&lt;p&gt;
    I found a new icon library: &lt;a href=&quot;https://css.gg&quot;&gt;CSS.gg&lt;/a&gt;. They serve
    very simple SVG icons you can just throw in your source. These have been
    sprinkled around where necessary.
&lt;/p&gt;

&lt;h2&gt;Code Blocks and Highlighting&lt;/h2&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;main&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-m&gt;println!&lt;/a-m&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;Here&amp;#39;s a code block!&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;);&lt;/a-p&gt;

    &lt;a-c&gt;// Explode&lt;/a-c&gt;
    &lt;a-k&gt;unsafe&lt;/a-k&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-m&gt;unreachable_unchecked!&lt;/a-m&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;
    I&#39;m currently using &lt;a href=&quot;https://prismjs.com/&quot;&gt;Prism.js&lt;/a&gt; for code
    highlighting. It&#39;s widely supported, has good theming, and a heck of a lot
    of language support. Um. The only issue is that it doesn&#39;t really make sense
    in &lt;samp&gt;$CURRENT_YEAR&lt;/samp&gt;. Since the advent of
    &lt;a href=&quot;https://tree-sitter.github.io/tree-sitter/&quot;&gt;tree-sitter&lt;/a&gt;, code
    highlighting is done via syntax trees instead of regex parsing. It&#39;s faster,
    and makes more sense. However: tree-sitter is written in a compiled
    language. This makes it a pain to run in the browser
    &lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_highlightssr&quot;&gt;[8]&lt;/a&gt;... There&#39;s web bindings via wasm
    &lt;a href=&quot;https://www.npmjs.com/package/web-tree-sitter&quot;&gt;on npm&lt;/a&gt;, but it
    would need a bit of work to use to highlight. Plus, I&#39;m sort of worried
    about the size of WASM blobs. Each language requires its own blob, so I
    could be pulling in several wasm blobs on every site. Prism.js is (at time
    of writing, on this site)
    &lt;math&gt;
        &lt;mrow&gt;
            &lt;mn&gt;74.4&lt;/mn&gt;
            &lt;mi&gt;kB&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mtext&gt;&lt;sub&gt;JS&lt;/sub&gt;&lt;/mtext&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mrow&gt;
            &lt;mn&gt;1.4&lt;/mn&gt;
            &lt;mi&gt;kB&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mtext&gt;&lt;sub&gt;CSS&lt;/sub&gt;&lt;/mtext&gt;
    &lt;/math&gt;
    big. As a test, I pulled in tree-sitter JS, WASM and the Javascript WASM
    blob. That alone (without any highlighting code or anything!) is
    &lt;math&gt;
        &lt;mrow&gt;
            &lt;mn&gt;152&lt;/mn&gt;
            &lt;mi&gt;kB&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mtext&gt;&lt;sub&gt;JS&lt;/sub&gt;&lt;/mtext&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mrow&gt;
            &lt;mn&gt;206&lt;/mn&gt;
            &lt;mi&gt;kB&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mtext&gt;&lt;sub&gt;WASM&lt;/sub&gt;&lt;/mtext&gt;
        &lt;mo&gt;+&lt;/mo&gt;
        &lt;mrow&gt;
            &lt;mn&gt;359&lt;/mn&gt;
            &lt;mi&gt;kB&lt;/mi&gt;
        &lt;/mrow&gt;
        &lt;mtext&gt;&lt;sub&gt;WASM js grammar&lt;/sub&gt;&lt;/mtext&gt; &lt;/math&gt;. Yipes!
&lt;/p&gt;

&lt;p&gt;
    So this is why I&#39;m using Prism. There are other regex-based highlighters,
    but this one works fine. One thing I added recently is the little language
    tag, which makes it more obvious which language is in the code block. I
    added it for my blog post where I talk about Swift in comparison to Rust.
&lt;/p&gt;

&lt;h2&gt;About footnotes&lt;/h2&gt;

&lt;p&gt;
    Umm. I like tangents. You might notice the large footnotes section on this
    article. This was pretty fun to implement! If you&#39;ve used markdown, and not
    really thought about writing in HTML, you might assume that footnotes are a
    HTML primitive. Nah! My current implementation looks like this:
&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    This is a paragraph &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;a&lt;/a-tg&gt; &lt;a-at&gt;ref&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;umm_actually&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;a&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;.
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;p&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;

&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;footer&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt; &lt;a-at&gt;id&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;notes_heading&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;Footnotes&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;h2&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;ol&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
        &lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;li&lt;/a-tg&gt; &lt;a-at&gt;id&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;note_umm_actually&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
            Well, there&amp;#39;s not really much text there. Its a sentence.
            Should it really even be put in a p tag?
            It&amp;#39;s kind of semantically wrong.
        &lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;li&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
    &lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;ol&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;a-p&gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;footer&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;
    A &lt;a href=&quot;https://j0.lol/static/js/footnotes.js&quot;&gt;script&lt;/a&gt; comes along and fills out
    everything to be &lt;em&gt;semantically correct&lt;/em&gt; HTML. It&#39;s based on this
    &lt;a href=&quot;https://www.niquette.ca/articles/accessible-footnotes/&quot;&gt;great article about making footnotes accessible&lt;/a&gt;, but I tweaked it a bit to remove markup boilerplate. Note that the
    &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag uses &lt;code&gt;ref&lt;/code&gt;, not &lt;code&gt;href&lt;/code&gt;. Not
    sure if this attribute is used in other places, but I thought it would be
    fine to use here. (It gets stripped by the script and replaced with a
    &lt;code&gt;href&lt;/code&gt;.)
&lt;/p&gt;

&lt;p&gt;
    One fun issue I found is that the &quot;return&quot; ↩ symbol looks different
    depending on browser. Mobile browsers (at least mine) will make it an emoji,
    while desktop browsers will use a normal font. You can choose this behaviour
    by suffixing a unicode character:
&lt;/p&gt;

&lt;style&gt;
    table {
        margin: 0 auto;
        border-collapse: collapse;
        border: 1px solid var(--border);
    }

    th,
    td {
        padding: 0.2rem;
        border: 1px solid var(--border);
    }
&lt;/style&gt;

&lt;figure&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;Default&lt;/th&gt;
            &lt;td&gt;↩&lt;/td&gt;
            &lt;td&gt;⛄&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;Variation Selector 15 &lt;code&gt;&amp;amp;#xfe0e;&lt;/code&gt;&lt;/th&gt;
            &lt;td&gt;↩&amp;#xfe0e;&lt;/td&gt;
            &lt;td&gt;⛄&amp;#xfe0e;&lt;/td&gt;
        &lt;/tr&gt;

        &lt;tr&gt;
            &lt;th&gt;Variation Selector 16 &lt;code&gt;&amp;amp;#xfe0f;&lt;/code&gt;&lt;/th&gt;

            &lt;td&gt;↩&amp;#xfe0f;&lt;/td&gt;
            &lt;td&gt;⛄&amp;#xfe0f;&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
    &lt;figcaption&gt;
        Weirdly, on Chrome macOS it always picks the snowman emoji.
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Aside on writing in HTML&lt;/h2&gt;

&lt;p&gt;
    I think HTML might be the most expressive way to write. You can express
    almost anything (see the &lt;a href=&quot;https://j0.lol/blog/love-your-website/#note_fonts&quot;&gt;fonts section&lt;/a&gt; :p) and it
    doesn&#39;t have the same issues that other markup formats have with wordiness
    (if you think HTML is bad, see
    &lt;math&gt;
        &lt;mrow&gt;
            &lt;mtext&gt;L&lt;/mtext&gt;
            &lt;mrow style=&quot;margin-left: -0.35em&quot;&gt;&lt;/mrow&gt;
            &lt;mpadded voffset=&quot;0.2em&quot; style=&quot;padding: 0.2em 0 0 0&quot;&gt;
                &lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;false&quot;&gt;
                    &lt;mstyle scriptlevel=&quot;1&quot; displaystyle=&quot;false&quot;&gt;
                        &lt;mtext&gt;A&lt;/mtext&gt;
                    &lt;/mstyle&gt;
                &lt;/mstyle&gt;
            &lt;/mpadded&gt;
            &lt;mrow style=&quot;margin-left: -0.15em&quot;&gt;&lt;/mrow&gt;
            &lt;mtext&gt;T&lt;/mtext&gt;
            &lt;mrow style=&quot;margin-left: -0.1667em&quot;&gt;&lt;/mrow&gt;
            &lt;mpadded voffset=&quot;-0.2155em&quot; style=&quot;padding: 0 0 0.2155em 0&quot;&gt;
                &lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;false&quot;&gt;
                    &lt;mtext&gt;E&lt;/mtext&gt;
                &lt;/mstyle&gt;
            &lt;/mpadded&gt;
            &lt;mrow style=&quot;margin-left: -0.125em&quot;&gt;&lt;/mrow&gt;
            &lt;mtext&gt;X&lt;/mtext&gt;
        &lt;/mrow&gt; &lt;/math&gt;.) I see most bloggers using markdown and I wonder: why? It makes
    everything harder. If you want to have a &quot;component&quot;, you basically have to
    &lt;a href=&quot;https://mdxjs.com/&quot;&gt;extend markdown&lt;/a&gt;, then have a framework or
    preprocessor to convert into HTML. You can put HTML in markdown (sometimes),
    but the syntax is very odd. Either way, it&#39;s gross.
&lt;/p&gt;

&lt;p&gt;
    I think people are scared of HTML. It&#39;s not that hard to write, and you can
    use &lt;a href=&quot;https://emmet.io/&quot;&gt;Emmet&lt;/a&gt; to write a lot of HTML from very
    understandable abbreviations if you have worked with CSS.
    &lt;code&gt;footer&gt;h2#notes_heading+ol&gt;li*3&lt;/code&gt; produces an entire footnotes
    section from earlier, and it lets you use tab to jump between text fields to
    fill.
&lt;/p&gt;

&lt;p&gt;
    You also lose a lot of
    &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Semantics&quot;&gt;semantic information&lt;/a&gt;
    with markdown. You could represent a
    &lt;code&gt;figure&gt;img+figcaption&lt;/code&gt;
    like this:
&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;md&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;![alt text](/static/foo.png)
This is an image.
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;
    But what happens when you want to style a figure? If you want to center the
    caption, frame the image, add a &lt;code&gt;::before&lt;/code&gt; pseudo-element with a
    counter, etc. What will a screen reader do when it comes across your figure?
    Not what you want it to do!
&lt;/p&gt;

&lt;p&gt;
    So it&#39;s either restrictive, ugly, or heavy. Um, maybe that was convincing. I
    think using HTML thoughtfully like this makes you better at using HTML
    thoughtfully elsewhere. When writing in HTML, go to
    &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements&quot;&gt;MDN&lt;/a&gt;
    and think: &quot;Is there an element for this?&quot; By doing this, you&#39;ll learn about
    new tags like &lt;code&gt;&amp;lt;samp&amp;gt;&lt;/code&gt; for quoting output from a program,
    &lt;code&gt;&amp;lt;abbr&amp;gt;&lt;/code&gt; for acronyms or abbreviations, all of MathML,
    &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; for displaying
    &lt;ruby&gt;pronounciation&lt;rp&gt;(&lt;/rp&gt;
        &lt;rt&gt;pro-noun-si-a-tion&lt;/rt&gt;
        &lt;rp&gt;)&lt;/rp&gt; &lt;/ruby&gt;. It&#39;s fun :&amp;rpar;
&lt;/p&gt;

&lt;h2&gt;Trying to conclude this post&lt;/h2&gt;

&lt;p&gt;
    I&#39;ve had a lot of fun writing this post, as well as working on my website.
    HTML+CSS is one of the most expressive technologies we have in software
    development. Working on your personal website is, I think, a form of self
    love. It&#39;s a construct built for the express purpose of self-expression.
    Express yourself!
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-happy&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
    I see you&#39;ve made it to the end. Thanks for sticking with me here, this is
    probably the longest blog post I have made yet. If you want to see more of
    my posts when they come out, &lt;a href=&quot;https://j0.lol/feed&quot;&gt;subscribe to my RSS feed&lt;/a&gt;!
    Due to how RSS is designed, I have literally no idea how many people are
    subscribed. But that&#39;s kind of fun!
&lt;/p&gt;
		&lt;/aside&gt;

&lt;!-- end  --&gt;
&lt;footer&gt;
    &lt;h2 id=&quot;notes_heading&quot;&gt;Footnotes&lt;/h2&gt;
    &lt;ol&gt;
        &lt;li id=&quot;note_physicalnotetaking&quot;&gt;
            I&#39;ve tried journaling physically before. I just really struggle with
            it. I think my hands aren&#39;t up to the task? They are very weak. I&#39;ve
            always struggled with it. I&#39;m left handed and I don&#39;t think I was
            ever raised to write properly? Maybe it&#39;s some deeper problem
            though. Oh well.
        &lt;/li&gt;
        &lt;li id=&quot;note_favicon&quot;&gt;
            Did you know that Safari is really &lt;em&gt;really&lt;/em&gt; annoying about
            favicon caching? Details are
            &lt;a href=&quot;https://www.leereamsnyder.com/favicons-in-2021#clearing-the-icon-cache-in-macos-safari-is-deeply-unpleasant&quot;&gt;here&lt;/a&gt;, but it really sucks. If you&#39;re trying to work on your favicon,
            see
            &lt;a href=&quot;https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs&quot;&gt;this great article&lt;/a&gt;
            about it, highlighted to me by one of my most favorite websites:
            &lt;a href=&quot;https://css-tricks.com/how-to-favicon-in-2021/&quot;&gt;CSS-Tricks&lt;/a&gt;.
        &lt;/li&gt;
        &lt;li id=&quot;note_whyfavicon&quot;&gt;
            Because they helped differentiate URLS in your... favorites. They
            are called favorites instead of bookmarks because Microsoft said so.
        &lt;/li&gt;

        &lt;li id=&quot;note_bookmarksaside&quot;&gt;
            Aside: I really don&#39;t use bookmarks. Maybe this really dates me as a
            member of Generation Z, but it&#39;s almost always a lot easier to just
            let the powerful browser autofill &lt;samp&gt;b&lt;/samp&gt; into
            &lt;samp&gt;bsky.app&lt;/samp&gt; for me. And the bookmarks toolbar is such a
            waste of space! If I don&#39;t have the URL saved in the browser for
            whatever reason, searching into Kagi will almost always give me what
            I want.
        &lt;/li&gt;
        &lt;li id=&quot;note_ie&quot;&gt;
            They were introduced in IE5. Makes sense why they use the very
            Windows &lt;samp&gt;.ico&lt;/samp&gt; format then.
        &lt;/li&gt;

        &lt;li id=&quot;note_googwebmanifest&quot;&gt;
            Google actually tried. When they introduced Progressive Web Apps,
            they made a new Web Manifest that you can stuff (Android) icons into
            instead of the head. Here&#39;s what that looks like:
    
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;json&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;{
  &lt;a-s&gt;&amp;quot;icons&amp;quot;&lt;/a-s&gt;: [
    { &lt;a-s&gt;&amp;quot;src&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;/icon-192.png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;type&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;image/png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;sizes&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;192x192&amp;quot;&lt;/a-s&gt; },
    { &lt;a-s&gt;&amp;quot;src&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;/icon-mask.png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;type&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;image/png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;sizes&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;512x512&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;purpose&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;maskable&amp;quot;&lt;/a-s&gt; },
    { &lt;a-s&gt;&amp;quot;src&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;/icon-512.png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;type&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;image/png&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;sizes&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;512x512&amp;quot;&lt;/a-s&gt; }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

            It would be nice if we could standardise on this for favicons or
            whatever. But everything has been set in stone, I guess.
        &lt;/li&gt;

        &lt;li id=&quot;note_shantell&quot; class=&quot;shantell&quot;&gt;
            Shantell Sans has a really lovely story. You can
            &lt;a href=&quot;https://shantellsans.com/process&quot;&gt;read it here&lt;/a&gt;.
        &lt;/li&gt;

        &lt;li id=&quot;note_highlightssr&quot;&gt;
            You may be asking: Why don&#39;t you just render it serverside? A few
            reasons:
            &lt;ul style=&quot;margin-block-end: 0.5rem&quot;&gt;
                &lt;li&gt;
                    My website is in PHP, not a compiled language like Rust.
                &lt;/li&gt;
                &lt;li&gt;
                    I did this &lt;a href=&quot;https://j0.lol/blog/php-site&quot;&gt;before&lt;/a&gt;, and made an
                    impossible-to-maintain PHP extension that sucks.
                &lt;/li&gt;
                &lt;li&gt;
                    Even if I did this, I&#39;m moving the highlighting computation
                    time onto the &quot;load bearing&quot; HTML step instead of loading
                    after the rest of the page is loaded.
                &lt;/li&gt;
                &lt;li&gt;
                    Like, I&#39;m not caching my pages (because PHP) so every page
                    gets rendered on every request. If I was making a static
                    site generator this would make a lot more sense. But I&#39;m not
                    really interested in re-architecting my entire website for
                    this purpose (yet...)
                &lt;/li&gt;
            &lt;/ul&gt;
            So, I&#39;m choosing to stick with client-side highlighting.
        &lt;/li&gt;
    &lt;/ol&gt;
&lt;/footer&gt;

&lt;style&gt;
    @import url(&quot;https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&amp;family=Playwrite+GB+S+Guides:ital@0;1&amp;family=Shantell+Sans:ital,wght@0,300..800;1,300..800&amp;display=swap&quot;);

    .monospace {
        font-family: &quot;Courier New&quot;, Courier, monospace;
    }

    .cursive {
        font-family: &quot;Playwrite GB S Guides&quot;, cursive;
        font-weight: 400;
        font-style: normal;
    }

    .shantell {
        font-family: &quot;Shantell Sans&quot;, cursive;
        font-optical-sizing: auto;
        font-weight: 400;
        font-style: normal;
        font-variation-settings:
            &quot;BNCE&quot; 0,
            &quot;INFM&quot; 0,
            &quot;SPAC&quot; 0;
    }

    .hstack {
        display: flex;
        flex-direction: row;
        width: 100%;
        gap: 0.5rem;
        justify-content: center;

        &gt; * {
            margin-left: 0;
            margin-right: 0;
        }
    }
&lt;/style&gt;
</content>
  </entry>
  <entry>
    <title>Is Swift becoming unergonomic Rust?</title>
    <link href="https://j0.lol/blog/swift-inline-array/" />
    <updated>2025-06-25T15:00:00Z</updated>
    <id>https://j0.lol/blog/swift-inline-array/</id>
    <content type="html">
		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
Going to preface this post with this: I&#39;m not a programming language
designer. I don&#39;t really know what I&#39;m talking about. I&#39;m just a Rust
developer with a passing interest in Swift. I probably made some mistakes in
here, feel free to send me corrections.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;
    So about a week ago, I was bored and watching WWDC 25 (pronounced &quot;dub dub
    DC&quot;, apparently.) I was watching this video:
    &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2025/312&quot;&gt;&lt;em&gt;Improve memory usage and performance with Swift&lt;/em&gt;&lt;/a&gt;, where they show off some new features in Swift to reduce memory overhead
    and increase safety (feel free to watch this alongside!) The first new
    standard library construct they show off is
    &lt;code&gt;InlineArray&amp;lt;N, T&amp;gt;&lt;/code&gt;, which acts like... an array. In Rust
    terms, the normal Swift array is similar to a
    &lt;code&gt;Arc&amp;lt;Vec&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;, where it is a growable collection that
    can be shared across threads and is copied on write. To keep the analogy
    going, the new Swift &lt;code&gt;InlineArray&lt;/code&gt; is like a Rust array:
    &lt;code&gt;[T; N]&lt;/code&gt;. To make it clear, &lt;code&gt;Array&lt;/code&gt; is heap-allocated
    and &lt;code&gt;InlineArray&lt;/code&gt; is stack-allocated.
&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;swift&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;func&lt;/a-k&gt; &lt;a-f&gt;array&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;var&lt;/a-k&gt; &lt;a-v&gt;array&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-n&gt;4&lt;/a-n&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-n&gt;5&lt;/a-n&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-n&gt;6&lt;/a-n&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-k&gt;func&lt;/a-k&gt; &lt;a-f&gt;inlineArray&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;var&lt;/a-k&gt; &lt;a-v&gt;array&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-t&gt;InlineArray&lt;/a-t&gt;&lt;a-o&gt;&amp;lt;&lt;/a-o&gt;&lt;a-n&gt;3&lt;/a-n&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;Int&lt;/a-v&gt;&lt;a-o&gt;&amp;gt;&lt;/a-o&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-n&gt;4&lt;/a-n&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-n&gt;5&lt;/a-n&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-n&gt;6&lt;/a-n&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

And let&#39;s compare to Rust:


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;vec&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-c&gt;// I could wrap this in an Arc, but&lt;/a-c&gt;
    &lt;a-c&gt;// that&amp;#39;s not really what real code would do.&lt;/a-c&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; &lt;a-k&gt;mut&lt;/a-k&gt; vec = &lt;a-m&gt;vec!&lt;/a-m&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-co&gt;4&lt;/a-co&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-co&gt;5&lt;/a-co&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-co&gt;6&lt;/a-co&gt;&lt;a-p&gt;];&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-k&gt;fn&lt;/a-k&gt; &lt;a-f&gt;array&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; &lt;a-k&gt;mut&lt;/a-k&gt; arr = &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-co&gt;4&lt;/a-co&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-co&gt;5&lt;/a-co&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-co&gt;6&lt;/a-co&gt;&lt;a-p&gt;];&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;
    You can probably see the argument I&#39;m going to make here — This is less
    ergonomic. Rust is often said to have bad syntax, but here you can see that
    the better performing option is &lt;em&gt;just easier&lt;/em&gt; than the heap-allocated
    version.
&lt;/p&gt;

&lt;h2&gt;Safety&lt;/h2&gt;

&lt;p&gt;
    So another topic in the video is how to efficiently read from a reference
    counted collection. In order to avoid the reference counter, and read data
    directly, one would previously use an unsafe pointer to do a direct read.
&lt;/p&gt;

    
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;swift&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-c&gt;// Safe usage of a buffer pointer&lt;/a-c&gt;
&lt;a-k&gt;func&lt;/a-k&gt; &lt;a-f&gt;processUsingBuffer&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;_&lt;/a-v&gt; &lt;a-v&gt;array&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-t&gt;Int&lt;/a-t&gt;&lt;a-p&gt;])&lt;/a-p&gt; &lt;a-o&gt;-&amp;gt;&lt;/a-o&gt; &lt;a-t&gt;Int&lt;/a-t&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-v&gt;array&lt;/a-v&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;withUnsafeBufferPointer&lt;/a-f&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-v&gt;buffer&lt;/a-v&gt; &lt;a-k&gt;in&lt;/a-k&gt;
        &lt;a-k&gt;var&lt;/a-k&gt; &lt;a-v&gt;result&lt;/a-v&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-n&gt;0&lt;/a-n&gt;
        &lt;a-k&gt;for&lt;/a-k&gt; &lt;a-v&gt;i&lt;/a-v&gt; &lt;a-k&gt;in&lt;/a-k&gt; &lt;a-n&gt;0&lt;/a-n&gt;&lt;a-o&gt;..&amp;amp;&lt;/a-o&gt;&lt;a-t&gt;lt&lt;/a-t&gt;&lt;a-p&gt;;&lt;/a-p&gt;buffer&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-v&gt;count&lt;/a-v&gt; &lt;a-p&gt;{&lt;/a-p&gt;
            &lt;a-v&gt;result&lt;/a-v&gt; &lt;a-o&gt;+=&lt;/a-o&gt; &lt;a-f&gt;calculate&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;using&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-v&gt;buffer&lt;/a-v&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-v&gt;at&lt;/a-v&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-v&gt;i&lt;/a-v&gt;&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;}&lt;/a-p&gt;
        &lt;a-k&gt;return&lt;/a-k&gt; &lt;a-v&gt;result&lt;/a-v&gt;
    &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;


&lt;p&gt;
    As highlighted in the talk, these buffers are unsafe partially because they
    could be used outside of the scope of the function (e.g. returned, or stored
    as part of a structure), and misued. In order to make this operation safe,
    they introduce a type called &lt;code&gt;Span&lt;/code&gt;, which points to a
    contiguious block of memory. In order to make this safe, a new &quot;Marker
    trait&quot; (Rust speak) called &lt;code&gt;Escapable&lt;/code&gt;
    is added to the language. This trait is auto-added to all types by default,
    and you can specify if your type cannot be used outside of its context with
    &lt;code&gt;~Escapable&lt;/code&gt;. Pretty neat language feature!
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
						So Swift calls traits &lt;em&gt;Protocols&lt;/em&gt;, and this one is implicitly
applied to all Swift types. Here&#39;s
&lt;a href=&quot;https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md&quot;&gt;SE-0446&lt;/a&gt;
if you want to read into it further. In Rust, we would make a trait,
and then have a
&lt;a href=&quot;https://doc.rust-lang.org/book/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods&quot;&gt;&lt;em&gt;blanket implementation&lt;/em&gt;&lt;/a&gt;
over a generic. Dunno how Swift does this!
&lt;!-- &lt;a aria-label=&quot;Return to content&quot;&gt;↩&lt;/a&gt; --&gt;

&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;
    This is a very different approach to Rust. We deal with pointer unsafety by
    relegating them to &lt;em&gt;Unsafe Rust&lt;/em&gt;, a language that lives alongside
    &lt;em&gt;Safe Rust&lt;/em&gt;. But also — We sidestep this whole problem.
    &lt;code&gt;Vec&lt;/code&gt;s are not reference-counted (you have to wrap them in a
    &lt;code&gt;Rc&lt;/code&gt; or &lt;code&gt;Arc&lt;/code&gt; for that,) so we don&#39;t have to sidestep
    the reference counter.
&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;
    You can clearly see the difference in these language&#39;s designs from these
    two examples. Swift makes working with types as easy as possible, while
    letting you &quot;drop down&quot; for performance. Rust instead makes the most
    ergonomic pattern the most performant, while letting you &quot;build up&quot;
    convenience by moving to heap allocation with &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt;,
    adding copy-on-write with &lt;code&gt;Cow&amp;lt;T&amp;gt;&lt;/code&gt;, or reference counting
    with &lt;code&gt;Rc&amp;lt;T&amp;gt;&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
    I think it&#39;s clear who these language features are for — Swift library
    maintainers. Being able to write more performant code for your users will
    make everyone happy, and doing this without unsafety is nice. I don&#39;t think
    this is really for app developers, or anything like that. Making all of your
    array accesses use spans to dodge the reference counter is
    &lt;em&gt;technically&lt;/em&gt; faster, but makes things much more annoying to maintain
    than the obvious solution. I think the talk makes this clear, as they show
    off a new
    &lt;a href=&quot;https://github.com/apple/swift-binary-parsing&quot;&gt;Binary Parsing library&lt;/a&gt;
    at the end which uses these new language features.
&lt;/p&gt;

&lt;p&gt;
    As for the title of this article. I don&#39;t think Swift is becoming
    unergonomic Rust (&lt;a href=&quot;https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines&quot;&gt;Betteridge&#39;s law of headlines&lt;/a&gt;
    wins again.) It&#39;s just bringing in features similar to Rust to &quot;drop down&quot;
    to. Honestly, a &quot;less complex&quot; Rust has a good place in the world of
    programming languages. There is some work adjacent to this, see the
    &lt;a href=&quot;https://github.com/softdevteam/alloy&quot;&gt;Alloy language&lt;/a&gt;: a fork of
    Rust that gives you opt-in garbage collection for things like Linked Lists.
    I think it&#39;s easy to write things like this off — but all languages have
    their tradeoffs. What makes you a good developer is being able to take all
    of the values of prospective systems and being able to pick the right one! (Bryan Cantrill has a great talk on this:
		&lt;a href=&quot;https://youtu.be/Xhx970_JKX4&quot;&gt;&lt;em&gt;Platform as a Reflection of Values&lt;/em&gt;&lt;/a&gt;)
&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>How I use the new Spotlight</title>
    <link href="https://j0.lol/blog/spotlight-raycast/" />
    <updated>2025-06-25T00:00:00Z</updated>
    <id>https://j0.lol/blog/spotlight-raycast/</id>
    <content type="html">&lt;p&gt;
    So. Raycast is a replacement for Apple&#39;s Spotlight, a quick search
    accessible anywhere in macOS with &lt;kbd&gt;⌘+Space&lt;/kbd&gt;. It is &quot;extendable&quot;, in
    that it has an in-app extensions library which add entries which do various
    things. This is useful in various ways, but I mostly use it to quick-search
    various websites, like Kagi or MDN.
&lt;/p&gt;

&lt;p&gt;
    In macOS Tahoe, Apple lets you run &quot;Shortcuts&quot; from Spotlight. These
    &lt;em&gt;can&lt;/em&gt;
    take a text input directly in Spotlight, and run basically whatever. One
    decent use for this is to run something like &lt;code&gt;brew update&lt;/code&gt; to
    update your packages.
&lt;/p&gt;
&lt;details&gt;
    &lt;summary&gt;What are &quot;Shortcuts&quot;?&lt;/summary&gt;

    Imagine if Scratch was integrated into your OS. You can organise blocks to
    make a kind of annoying form of computing. It has strange limitations.
&lt;/details&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/spotlight-raycast/BBx68GarG3-1500.webp 1500w&quot;&gt;&lt;img class=&quot;applerounded blackbg&quot; width=&quot;1500&quot; height=&quot;800&quot; alt=&quot;A screenshot of the Shortcuts app showing a basic procedure to search a website from an input.&quot; src=&quot;https://j0.lol/blog/spotlight-raycast/BBx68GarG3-1500.jpeg&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;A simple shortcut. Useful, though!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
    One neat thing is that you can bind a shortcut to a.... shortcut? Like, they
    call this &quot;Quick Keys&quot;, but it&#39;s like a shortcut alias. If I want to search
    Kagi, I can make a &lt;code&gt;kg&lt;/code&gt; alias, then &lt;kbd&gt;kg↩︎Foo&lt;/kbd&gt; to search
    &quot;Foo&quot; on Kagi.
&lt;/p&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/spotlight-raycast/Ue02YN-hDB-1284.webp 1284w&quot;&gt;&lt;img class=&quot;superrounded blackbg&quot; width=&quot;1284&quot; height=&quot;116&quot; alt=&quot;A screenshot of Spotlight showing a shortcut asking for input.&quot; src=&quot;https://j0.lol/blog/spotlight-raycast/Ue02YN-hDB-1284.jpeg&quot;&gt;&lt;/picture&gt;

&lt;p&gt;
    That&#39;s basically it. Shortcuts are kind of fun to make, but can be a bit
    infuriating if you don&#39;t know how to use the esoteric blocks. On macOS, you
    can at least fall back to &lt;code&gt;sh&lt;/code&gt;, or a scripting language like
    Python.
&lt;/p&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/spotlight-raycast/_5Jjg-dvoo-1500.webp 1500w&quot;&gt;&lt;img class=&quot;applerounded blackbg&quot; width=&quot;1500&quot; height=&quot;800&quot; alt=&quot;A screenshot of the Shortcuts app with all the Spotlight shortcuts.&quot; src=&quot;https://j0.lol/blog/spotlight-raycast/_5Jjg-dvoo-1500.jpeg&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;
        Here&#39;s all the shortcuts I&#39;ve made so far. Can&#39;t really think of any
        more I could make.
    &lt;/figcaption&gt;
&lt;/figure&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
    This kinda works for me. There&#39;s definitely more that Raycast can do that
    Shortcuts can&#39;t right now, but it&#39;s pretty decent right now! Definitely
    enough for me to stop using it. Your use case is probably different, though.
    I know a lot of people would hate a workflow like this, and prefer to use
    the browser and/or the CLI.
&lt;/p&gt;
		&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>The macOS Tahoe beta is so bad that it made me switch to Chromium.</title>
    <link href="https://j0.lol/blog/tahoe-beta/" />
    <updated>2025-06-24T22:00:00Z</updated>
    <id>https://j0.lol/blog/tahoe-beta/</id>
    <content type="html">&lt;p&gt;
    Every damn year I do this. I see new beta, I install beta, beta sucks, I
    cry. Why do I keep doing it?
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-worried&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
It looks pretty, though...
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;
    Yeah... So basically &lt;em&gt;everything&lt;/em&gt; here is worse. I don&#39;t really care
    to have &quot;discourse&quot; about the new design, but it sure seems to make even
    basic things like Finder run slow somehow.
&lt;/p&gt;

&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/tahoe-beta/p266ElCUX3-1840.webp 1840w&quot;&gt;&lt;img class=&quot;applerounded blackbg&quot; width=&quot;1840&quot; height=&quot;872&quot; alt=&quot;Screenshot of the new Finder application in macOS Tahoe&quot; src=&quot;https://j0.lol/blog/tahoe-beta/p266ElCUX3-1840.jpeg&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;
        It has some really rough edges too... What even is this? I think the
        glowing &quot;tab bar&quot; is the most offensive thing here.
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;
    One issue is Safari is &lt;em&gt;really&lt;/em&gt; slow. Embarrassingly slow. I&#39;ve long
    been an advocate for not using Chromium, since Google is the devil or
    whatever. But the new Safari update makes using WebKit damn near unusable.
    It can take like, upwards of 30 seconds to open a new tab sometimes. Maybe
    my 8GB M1 Macbook Pro is showing its (rather young) age, but I don&#39;t think
    it&#39;s just me.
&lt;/p&gt;

&lt;p&gt;
    So I decided to make a tentative deal with the devil. I&#39;m currently using...
    Thorium. It&#39;s &lt;a href=&quot;https://thorium.rocks/&quot;&gt;some chrome fork&lt;/a&gt; that a friend
    recommended to me. It claims to be
    &lt;em&gt;&#39;The fastest browser on Earth.&#39;&lt;/em&gt; Whatever. I just want a no-nonsense
    browser that won&#39;t freeze for damn seconds while I work. For that purpose,
    it has been very good.
&lt;/p&gt;

&lt;p&gt;
    Why not Firefox? Mainly because it&#39;s the only macOS browser that doesn&#39;t
    have
    &lt;em&gt;by default&lt;/em&gt;
    WebGPU support somehow, and that&#39;s like what 80% of what I&#39;m interested in
    right now.
&lt;/p&gt;

&lt;p&gt;
    The new Spotlight is pretty good, though. I&#39;ve been having fun making
    shortcuts for it. It has the potential to replace Raycast fully (but
    Spotlight has been quite buggy with shortcuts as of now.)
&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
        As of writing, I have not installed macOS 26 Beta 2. Could fix everything!
        Who knows. Might update this post if anything interesting happens on this
        front. That&#39;s all I wanted to blog about, though. See ya!

				&lt;br&gt;&lt;br&gt;
				&lt;strong&gt;2026 Update:&lt;/strong&gt; They did not fix it... I&#39;m currently using Firefox instead of Helium at least! Because they support WebGPU now!
&lt;/p&gt;
		&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>I added Bluesky powered comments!</title>
    <link href="https://j0.lol/blog/comments/" />
    <updated>2025-03-26T20:00:00Z</updated>
    <id>https://j0.lol/blog/comments/</id>
    <content type="html">&lt;p&gt;I added comments to my site!&lt;/p&gt;

&lt;p&gt;It works by linking to a bluesky post, and mirroring the comments to my website. It&#39;s pretty cool :)&lt;/p&gt;

&lt;p&gt;Here&#39;s the basics of how to use it:&lt;/p&gt;

		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;html&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-c&gt;&amp;lt;!-- You should download and mirror the script if possible! --&amp;gt;&lt;/a-c&gt;
&lt;a-c&gt;&amp;lt;!-- https://github.com/LoueeD/bsky/blob/main/comments.js --&amp;gt;&lt;/a-c&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;script&lt;/a-tg&gt; &lt;a-at&gt;type&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;module&lt;/a-s&gt;&amp;quot; &lt;a-at&gt;src&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;https://esm.sh/gh/loueed/bsky@v1.0.0/comments&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;script&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;

&lt;a-c&gt;&amp;lt;!-- You can plug your post URL into https://pdsls.dev to get the URI here. --&amp;gt;&lt;/a-c&gt;
&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-tg&gt;bsky-comments&lt;/a-tg&gt; &lt;a-at&gt;post&lt;/a-at&gt;=&amp;quot;&lt;a-s&gt;at://did:plc:by6abidhbnj6siox5if2wzq6/app.bsky.feed.post/3llckts3ryc2w&lt;/a-s&gt;&amp;quot;&lt;a-p&gt;&amp;gt;&amp;lt;/&lt;/a-p&gt;&lt;a-tg&gt;bsky-comments&lt;/a-tg&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;Yeah, that&#39;s basically it. I didn&#39;t make it or anything. Just want to show it off.&lt;/p&gt;

&lt;p&gt;The only issue is... how am I going to post this and get the ATproto uri before I publish it?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to run games on macOS like it&#39;s SteamOS</title>
    <link href="https://j0.lol/blog/kegworks-winery/" />
    <updated>2025-03-22T19:00:00Z</updated>
    <id>https://j0.lol/blog/kegworks-winery/</id>
    <content type="html">

		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
None of these images contain alt text, I do apologise. This is very diagram heavy and it is hard to transcribe. None of the images should be &lt;em&gt;necessary&lt;/em&gt; for following the article.
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;
    Whisky was recently pronounced dead
    &lt;sup&gt;&lt;a href=&quot;https://www.reddit.com/r/macgaming/comments/1j6agur/comment/mgt88tb/?context=3&amp;utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button&quot;&gt;[src]&lt;/a&gt;&lt;/sup&gt;. This was inevitable. It wasn&#39;t really that good either. However, this
    leaves a void in the macOS gaming community of Wine GUIs that don&#39;t cost an
    arm and a leg.
&lt;/p&gt;

&lt;p&gt;
    However, a recent revival of Wineskin has come out, called Kegworks Winery.
    It&#39;s pretty decent. Here&#39;s a tutorial on how to use it.
&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;ol&gt;
    &lt;li&gt;
        Install Kegworks Winery. Here&#39;s the
        &lt;a href=&quot;https://brew.sh&quot;&gt;brew&lt;/a&gt; command they have on their GitHub.
        &lt;sup&gt;&lt;a href=&quot;https://github.com/Kegworks-App&quot;&gt;[src]&lt;/a&gt;&lt;/sup&gt;

        
		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;sh&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-f&gt;brew&lt;/a-f&gt; upgrade
&lt;a-f&gt;brew&lt;/a-f&gt; install &lt;a-co&gt;--cask&lt;/a-co&gt; &lt;a-co&gt;--no-quarantine&lt;/a-co&gt; Kegworks-App/kegworks/kegworks
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
    &lt;/li&gt;

    &lt;li&gt;
        Open it up and install a version of CrossOver wine
        (&lt;samp&gt;WS**WineCX**&lt;/samp&gt;)
    &lt;/li&gt;

    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/kegworks-winery/Wssf9bULZ9-404.webp 404w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/kegworks-winery/Wssf9bULZ9-404.jpeg&quot; height=&quot;488&quot; width=&quot;404&quot; alt=&quot;&quot;&gt;&lt;/picture&gt;
    &lt;li&gt;
        Make a wrapper app (Be prepared for this to take a while, and freeze
        up.)
    &lt;/li&gt;

    &lt;li&gt;Open up your new wrapper&lt;/li&gt;
    &lt;li&gt;
        Notice that your new wrapper application has an AI generated icon. Ew.
        Change that to something better. (&lt;a href=&quot;https://vps.j0.lol/website-assets/kegwroks/Steam.icns&quot;&gt;Here&#39;s a Steam icon.&lt;/a&gt;)
    &lt;/li&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/kegworks-winery/ctfm7_TBao-685.webp 685w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/kegworks-winery/ctfm7_TBao-685.jpeg&quot; alt=&quot;&quot; width=&quot;685&quot; height=&quot;369&quot;&gt;&lt;/picture&gt;
&lt;/ol&gt;

&lt;h2&gt;Steam&lt;/h2&gt;
&lt;ol&gt;
    &lt;li&gt;
        You want to run steam. Probably. Open winetricks and run the steam
        action.
    &lt;/li&gt;

    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/kegworks-winery/OfK6ny7uGC-803.webp 803w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/kegworks-winery/OfK6ny7uGC-803.jpeg&quot; alt=&quot;&quot; height=&quot;727&quot; width=&quot;803&quot;&gt;&lt;/picture&gt;
    &lt;li&gt;
        Once that&#39;s done, go to &#39;Advanced&#39; and change the Windows app to Steam.

        &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/kegworks-winery/BRlA37f5hO-685.webp 685w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/kegworks-winery/BRlA37f5hO-685.jpeg&quot; width=&quot;685&quot; height=&quot;369&quot; alt=&quot;&quot;&gt;&lt;/picture&gt;
    &lt;/li&gt;
    &lt;li&gt;
        You will want to use a DirectX translation layer:
        &lt;ul&gt;
            &lt;li&gt;
                D3DMetal is the official, Apple supported, translation layer.
                This will have the least overhead and, usually, widest
                compatability. If you&#39;re unsure, use this one.
            &lt;/li&gt;
            &lt;li&gt;
                DXVK is used widely on macOS and Linux for gpu call translation.
                This should be your fallback if D3DMetal fails you.
            &lt;/li&gt;
            &lt;li&gt;
                DXMT is a newcomer and focuses only on DirectX11 to Metal
                translation. Some games will work great with this.
                &lt;sup&gt;&lt;a href=&quot;https://support.codeweavers.com/advanced-settings-in-crossover-235&quot;&gt;[src]&lt;/a&gt;&lt;/sup&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Tweaking&lt;/h2&gt;
&lt;p&gt;You may also want to tweak some other graphics settings:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;
        You may get better or worse performance by tweaking which type of
        synchronization you use. There are two options in Kegworks and
        Crossover. Personally, I have not seen any difference between the two.

        &lt;ul&gt;
            &lt;li&gt;Eventfd-based synchronization, or &lt;samp&gt;ESYNC&lt;/samp&gt;.&lt;/li&gt;
            &lt;li&gt;
                Mach semaphore-based synchronization, or &lt;samp&gt;MSYNC&lt;/samp&gt;
                &lt;sup&gt;&lt;a href=&quot;https://support.codeweavers.com/advanced-settings-in-crossover-235&quot;&gt;[src]&lt;/a&gt;&lt;/sup&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
        High DPI mode. If you&#39;re on an Apple display, you probably have a high
        DPI. This is quite annoying to do.
        &lt;ol&gt;
            &lt;li&gt;Open &lt;samp&gt;regedit&lt;/samp&gt;&lt;/li&gt;
            &lt;li&gt;Go to &lt;samp&gt;HKCU&#92;Software&#92;Wine&#92;Mac Driver&lt;/samp&gt;&lt;/li&gt;
            &lt;li&gt;
                Then set a new string value &lt;samp&gt;RetinaMode&lt;/samp&gt; to
                &lt;samp&gt;Y&lt;/samp&gt;
                &lt;sup&gt;&lt;a href=&quot;https://www.reddit.com/r/macgaming/comments/1gees5s/comment/lu9dlsx/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button&quot;&gt;[src]&lt;/a&gt;&lt;/sup&gt;
            &lt;/li&gt;
            &lt;li&gt;Open &lt;samp&gt;winecfg&lt;/samp&gt;&lt;/li&gt;
            &lt;li&gt;
                Change the dpi. Note that the default is 96dpi. For a 2&amp;times;
                DPI monitor, go for &lt;small&gt;96 &amp;times; 2 = &lt;/small&gt; 192dpi.
            &lt;/li&gt;
        &lt;/ol&gt;

        If you&#39;re often switching between high dpi and 96dpi monitors, I would
        recommend making a separate wrapper app so you don&#39;t have to undo and
        redo this hack constantly.

        &lt;br&gt;&lt;br&gt;
        &lt;b&gt;EDIT:&lt;/b&gt; I made a tool to automate this:
        &lt;a href=&quot;https://github.com/j0lol/winebottle-retina-toggler&quot;&gt;winebottle-retina-toggler&lt;/a&gt;. I also made an
        &lt;a href=&quot;https://github.com/Kegworks-App/Kegworks/issues/79&quot;&gt;issue on Kegworks Winery&lt;/a&gt;
        in hopes of a better solution.
    &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;p&gt;
Theoretically, Steam should &quot;just work.&quot; Give it a test run before you close
your wrapper app. If your wrapper stops working, open up your wrapper
&lt;samp&gt;.app&lt;/samp&gt; with &quot;Show Package Contents&quot; and open
&lt;samp&gt;KegworksConfig.app&lt;/samp&gt; to get access to the config menu again.&lt;/p&gt;

&lt;p&gt;
    Annoyingly, Steam is quite buggy in wine. This is just because Steam sucks.
    It&#39;s a web browser running through three translation layers (D3DMetal, Wine,
    Rosetta 2.) It is still pretty usable, though, and the macOS build of Steam
    isn&#39;t much better!
&lt;/p&gt;

&lt;h2&gt;Performance and Compatibility&lt;/h2&gt;
&lt;p&gt;
It&#39;s alright. The main issue is memory constraints. If you have an 8gb Apple
device, you will feel this when you try and load large games. If you want good
benchmarks, I can recommend
&lt;a href=&quot;https://www.youtube.com/@Andytizer&quot;&gt;Andrew Tsai&lt;/a&gt;&#39;s channel. If you
need other performance &amp; compatability details, try the
&lt;a href=&quot;https://www.applegamingwiki.com/wiki/Home&quot;&gt;AppleGamingWiki&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
This kinda sucks, right? Why can&#39;t Valve Corporation just do this for us? Who knows. I think they just hate us macOS users.
&lt;/p&gt;
		&lt;/aside&gt;
</content>
  </entry>
  <entry>
    <title>Free yourself from the shackles of productivity</title>
    <link href="https://j0.lol/blog/productivity/" />
    <updated>2025-03-21T12:00:00Z</updated>
    <id>https://j0.lol/blog/productivity/</id>
    <content type="html">&lt;p&gt;
    One of the things that has been plaguing me recently is editor choice. I&#39;ve
    been a programmer for a large portion of my life, and the editor is the
    window to code. Naturally, I have tried a lot of editors:
&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;IntelliJ and friends&lt;/li&gt;
    &lt;li&gt;VSCode&lt;/li&gt;
    &lt;li&gt;NVIM&lt;/li&gt;
    &lt;li&gt;Helix&lt;/li&gt;
    &lt;li&gt;Emacs&lt;/li&gt;
    &lt;li&gt;Nova&lt;/li&gt;
    &lt;li&gt;Zed&lt;/li&gt;
    &lt;li&gt;Micro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    It is easy to switch editor. Most editors use the same
    &lt;abbr&gt;LSP&lt;/abbr&gt; backends (IntelliJ is an exception!) So why switch?
&lt;/p&gt;
&lt;h2&gt;&amp;quot;Modal Editing is more productive&amp;quot;&lt;/h2&gt;
&lt;p&gt;
    My first big switch was to NVIM. I was instantly sold on the idea that I
    could use my keyboard to do actions quicker. And you can! After learning how
    to use vim keybinds, you can do a lot of actions very quickly. Is this good,
    though? One can quite easily see the link between &#39;actions per second&#39; and
    productivity, but I would like to argue that this is not useful.
&lt;/p&gt;
&lt;h2&gt;&amp;quot;Helix has easier keybinds&amp;quot;&lt;/h2&gt;
&lt;p&gt;What sold me on Helix, a TUI editor made in Rust, was two things:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;It claims to have keybinds that make more &amp;quot;sense&amp;quot;&lt;/li&gt;
    &lt;li&gt;It claims to have faster actions per second than vim keybinds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
    Instead of having an &amp;quot;action to selection&amp;quot; model, it has a
    &amp;quot;selection to action&amp;quot; model, which supposedly makes it more
    intuitive. The issue here is that &lt;em&gt;productivity&lt;/em&gt; and
    &lt;em&gt;intuitiveness&lt;/em&gt; are kind of fake words in situations like this.
    Nobody has stats on this! The developers chose to make it like this because
    they thought it was better. And that&#39;s fine! The issue is, none of this is
    objective in any way. You are not a better programmer for using a modal
    editor. Even if modal editing was an objective good, developer productivity
    is not a concrete measurement, and probably is influenced by a wide amount
    of variables. Caffeine intake, or hours slept, is probably a better measure
    of productivity than editor.
&lt;/p&gt;
&lt;h2&gt;It doesn&#39;t matter if there is a &quot;best editor&quot; or not&lt;/h2&gt;
&lt;p&gt;
    That&#39;s my thesis. All editors are, functionally, the same program. You can
    do whatever you want in any editor about as fast as any other editor. I will
    not defend IntelliJ over VSCode, or anything similar, because they are the
    same. Stop taking editing so seriously, and relax!
&lt;/p&gt;
&lt;figure&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/productivity/92ON2sYWcO-1920.webp 1920w&quot;&gt;&lt;img width=&quot;1920&quot; height=&quot;1144&quot; alt=&quot;Screenshot of PhpStorm with Comic Sans MS font in the UI.&quot; src=&quot;https://j0.lol/blog/productivity/92ON2sYWcO-1920.jpeg&quot;&gt;&lt;/picture&gt;
    &lt;figcaption&gt;Can you tell I don&#39;t care about seriousness?&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;But...&lt;/h2&gt;
&lt;blockquote&gt;
    &lt;p&gt;But I like using &lt;samp&gt;$EDITOR&lt;/samp&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
    That&#39;s good! I&#39;m not on a crusade. I think discussion just orients around
    &quot;productivity&quot; and other such programming metrics too much in editor
    conversation. I think people tend to forget that we are human sometimes! If
    &lt;samp&gt;$EDITOR&lt;/samp&gt; is what makes you feel good, go for it!
&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>My site went down!</title>
    <link href="https://j0.lol/blog/downtime/" />
    <updated>2025-03-20T22:00:00Z</updated>
    <id>https://j0.lol/blog/downtime/</id>
    <content type="html">&lt;p&gt;
    My site went down! This is because the syntax highlighting library I made in
    an &lt;a href=&quot;https://j0.lol/blog/php-site&quot;&gt;older blog post&lt;/a&gt; broke! Instead of just
    updating it (it was a pain because &lt;code&gt;ext-php-rs&lt;/code&gt; is hard to
    use...) I just replaced it with &lt;a href=&quot;https://prismjs.com&quot;&gt;Prism.JS&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
    This library is a lot simpler and better maintained. The only downside is
    that highlighting happens on the frontend, which probably means some sort of
    &lt;abbr title=&quot;Flash of Un-styled Content&quot;&gt;FOUC&lt;/abbr&gt; happens. The benefit is
    that I don&#39;t have to maintain a PHP library in Rust.
&lt;/p&gt;

&lt;p&gt;
    While doing this, I realised that I could make a lot of my site a lot easier
    to maintain if I just drop build steps.
&lt;/p&gt;

&lt;h2&gt;Blogging in HTML&lt;/h2&gt;

&lt;p&gt;
    It&#39;s not that hard! The downsides of having to use XML-y tags everywhere is
    that you gain the benefits of concrete syntax. Another benefit is
    &lt;em&gt;Semantic HTML&lt;/em&gt;, which let&#39;s me do things like
    &lt;abbr title=&quot;Abbreviations&quot;&gt;abbr&#39;s&lt;/abbr&gt;.
&lt;/p&gt;

&lt;p&gt;
    It&#39;s pretty fun to read all the semantic tags and try them out. And it&#39;s fun
    to read the MDN!
    &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot;&gt;Click here&lt;/a&gt;
    for a combination of both of these. &lt;samp&gt;=)&lt;/samp&gt;
&lt;/p&gt;

&lt;h2&gt;Styling&lt;/h2&gt;

&lt;p&gt;
    I changed the font on my website from Cascadia Code to Maple Mono. I think
    it complements the body font a lot better! And I use the font in my editor
    now instead of Cascadia.
&lt;/p&gt;

&lt;p&gt;
    While changing styling, I found a horrible bug where a
    &lt;code&gt;&amp;#x3C;pre&amp;#x3E;&amp;#x3C;code&amp;#x3E;&lt;/code&gt; block&#39;s contents would
    influence the size of the document&#39;s page, even when scrolling. To fix this,
    my partner figured out that because I was using a
    &lt;code&gt;display: grid;&lt;/code&gt; element as a document wrapper, the grid was
    calculating it&#39;s &lt;code&gt;min-width&lt;/code&gt; with the minimum size of all
    elements in the body.
&lt;/p&gt;

&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/downtime/BUepFlp_AD-1518.webp 1518w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/downtime/BUepFlp_AD-1518.jpeg&quot; alt=&quot;Crude diagram of above explanation.&quot; width=&quot;1518&quot; height=&quot;938&quot;&gt;&lt;/picture&gt;

&lt;p&gt;The fix, of course, is trivial:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;wrapper&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;display&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; grid&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;grid-template-rows&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; auto &lt;a-n&gt;1&lt;/a-n&gt;&lt;a-t&gt;fr&lt;/a-t&gt; auto&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;

&lt;a-tg&gt;main&lt;/a-tg&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-c&gt;/*
        Needed, otherwise .wrapper grid will use `min-width: min-content`.
        Problematic if an element expands past 100vw,
        where it will cause main to grow massively.

        https://developer.mozilla.org/en-US/docs/Web/CSS/min-width#values
    */&lt;/a-c&gt;
    &lt;a-pr&gt;min-width&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;0&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;h3&gt;Dialog box&lt;/h3&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
        This was pretty fun to implement! I used the MDN
        &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_backgrounds_and_borders/Border-image_generator&quot;&gt;border-image generator&lt;/a&gt;
        to make the dialog box, then I drew the deer on the left :-)
	&lt;small&gt;(this looks a bit different, see the &lt;a href=&quot;https://web.archive.org/web/20250808085504/j0.lol/blog/downtime&quot;&gt;old version of the website&lt;/a&gt; maybe?)&lt;/small&gt;
&lt;/p&gt;
		&lt;/aside&gt;

&lt;p&gt;
    That&#39;s about it, other than some other stylesheet cleanup (I removed a bunch
    of &lt;code&gt;calc&lt;/code&gt;s and moved some &lt;code&gt;px&lt;/code&gt; values to
    &lt;code&gt;rem&lt;/code&gt;!) Keep things simple!
&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>RSS is done!</title>
    <link href="https://j0.lol/blog/rss-is-done/" />
    <updated>2024-10-10T20:00:00Z</updated>
    <id>https://j0.lol/blog/rss-is-done/</id>
    <content type="html">&lt;p&gt;Just a short post!&lt;/p&gt;

&lt;p&gt;I added RSS (technically an Atom feed) to my website!
    You can now subscribe to my blog posts with the link on my &lt;a href=&quot;https://j0.lol/blog&quot;&gt;blog index&lt;/a&gt;
    or click &lt;a href=&quot;https://j0.lol/feed&quot;&gt;here&lt;/a&gt; to add me to your RSS reader.&lt;/p&gt;

&lt;h2&gt;Implementation&lt;/h2&gt;

&lt;p&gt;I used this very colorful library from &quot;Starbeam Rainbow Labs&quot;: &lt;a href=&quot;https://starbeamrainbowlabs.com/code/phpatomgenerator/&quot;&gt;atom.gen.php&lt;/a&gt;.
    It&#39;s quite easy to use, and less buggy than others that I found.&lt;/p&gt;

&lt;p&gt;I had to pull in &lt;a href=&quot;https://github.com/briannesbitt/Carbon&quot;&gt;Carbon&lt;/a&gt; (great library) to deal with dates better too
    (I was storing them in a quite silly way before.)&lt;/p&gt;

&lt;p&gt;Pretty simple, don&#39;t you think?&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;php&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-tg&gt;&amp;lt;?php&lt;/a-tg&gt;

&lt;a-k&gt;use&lt;/a-k&gt; &lt;a-ns&gt;Michelf&lt;/a-ns&gt;&#92;&lt;a-t&gt;MarkdownExtra&lt;/a-t&gt;;
&lt;a-k&gt;require_once&lt;/a-k&gt; &lt;a-s&gt;&amp;quot;remote/atom.gen.php&amp;quot;&lt;/a-s&gt;; &lt;a-c&gt;# https://starbeamrainbowlabs.com/code/phpatomgenerator/&lt;/a-c&gt;
&lt;a-k&gt;global&lt;/a-k&gt; &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;posts&lt;/a-v&gt;;
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;host&lt;/a-v&gt; = &lt;a-s&gt;&amp;quot;https://j0.lol&amp;quot;&lt;/a-s&gt;; &lt;a-c&gt;// So this doesn&amp;#39;t break when im working on localhost&lt;/a-c&gt;

&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt; = &lt;a-k&gt;new&lt;/a-k&gt; &lt;a-cr&gt;atomfeed&lt;/a-cr&gt;();
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-pr&gt;title&lt;/a-pr&gt; = &lt;a-s&gt;&amp;quot;j0&amp;#39;s blog&amp;quot;&lt;/a-s&gt;;
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-pr&gt;id_uri&lt;/a-pr&gt; = &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;host&lt;/a-v&gt; . &lt;a-s&gt;&amp;quot;/blog&amp;quot;&lt;/a-s&gt;;
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-pr&gt;feed_uri&lt;/a-pr&gt; = &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;host&lt;/a-v&gt; . &lt;a-s&gt;&amp;quot;/feed&amp;quot;&lt;/a-s&gt;;
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-pr&gt;logo_uri&lt;/a-pr&gt; = &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;host&lt;/a-v&gt; . &lt;a-s&gt;&amp;quot;/static/j0site-pfp.png&amp;quot;&lt;/a-s&gt;;
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-f&gt;addauthor&lt;/a-f&gt;(&lt;a-s&gt;&amp;quot;Jo Null&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;me@j0.lol&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;https://j0.lol/&amp;quot;&lt;/a-s&gt;, &lt;a-s&gt;&amp;quot;author&amp;quot;&lt;/a-s&gt;);

&lt;a-k&gt;foreach&lt;/a-k&gt; (&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;posts&lt;/a-v&gt; &lt;a-k&gt;as&lt;/a-k&gt; &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;slug&lt;/a-v&gt; =&amp;gt; &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;post&lt;/a-v&gt;) {
    &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;parser&lt;/a-v&gt; = &lt;a-k&gt;new&lt;/a-k&gt; &lt;a-cr&gt;MarkdownExtra&lt;/a-cr&gt;();
    &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;content&lt;/a-v&gt; = &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;parser&lt;/a-v&gt;-&amp;gt;&lt;a-f&gt;transform&lt;/a-f&gt;(
        &lt;a-f&gt;file_get_contents&lt;/a-f&gt;(&lt;a-s&gt;&amp;quot;./posts/&amp;quot;&lt;/a-s&gt; . &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;slug&lt;/a-v&gt; . &lt;a-s&gt;&amp;quot;.md&amp;quot;&lt;/a-s&gt;)
    );

    &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-f&gt;addentry&lt;/a-f&gt;(
        &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;host&lt;/a-v&gt; . &lt;a-s&gt;&amp;quot;/blog/&amp;quot;&lt;/a-s&gt; . &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;slug&lt;/a-v&gt;,
        &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;post&lt;/a-v&gt;[&lt;a-s&gt;&amp;quot;title&amp;quot;&lt;/a-s&gt;],
        &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;post&lt;/a-v&gt;[&lt;a-s&gt;&amp;quot;date&amp;quot;&lt;/a-s&gt;]-&amp;gt;&lt;a-f&gt;getTimestamp&lt;/a-f&gt;(),
        &lt;a-s&gt;&amp;quot;Jo Null&amp;quot;&lt;/a-s&gt;,
        &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;content&lt;/a-v&gt;
    );
}

&lt;a-f&gt;header&lt;/a-f&gt;(&lt;a-s&gt;&amp;quot;content-type: application/atom+xml&amp;quot;&lt;/a-s&gt;);
&lt;a-k&gt;echo&lt;/a-k&gt; &lt;a-o&gt;$&lt;/a-o&gt;&lt;a-v&gt;feed&lt;/a-v&gt;-&amp;gt;&lt;a-f&gt;render&lt;/a-f&gt;();
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <title>When to use Rust</title>
    <link href="https://j0.lol/blog/when-to-use-rust/" />
    <updated>2024-09-29T12:00:00Z</updated>
    <id>https://j0.lol/blog/when-to-use-rust/</id>
    <content type="html">&lt;p&gt;Part of using a tool is to decide when to use it instead of using another tool.
    I think we should look at programming languages the same. No language will ever be &quot;one size fits all&quot;,
    as they all have small differences that will make them better for different use cases.&lt;/p&gt;

&lt;p&gt;Some examples of when to use Rust:&lt;/p&gt;

&lt;h2&gt;When you&#39;re doing low-level development&lt;/h2&gt;

&lt;p&gt;If you&#39;re working low level, and you want your code to be correct, use Rust.
    This one&#39;s pretty obvious. I like doing a lot of low-level game development (working on obsolete hardware, writing shaders, etc.) and Rust is great for this as it has lots of great tools and frameworks for this!&lt;/p&gt;

&lt;h2&gt;When you&#39;re writing performant code&lt;/h2&gt;

&lt;p&gt;This is basically the same as the previous point, but Rust&#39;s &quot;zero cost abstractions&quot; mean that you can work at a
    higher level and write performant code. Sometimes.&lt;/p&gt;

&lt;h2&gt;When you need something to be airtight&lt;/h2&gt;

&lt;p&gt;One great example of this is Element X, a Matrix client.
    Matrix rolls their own encryption, and while I don&#39;t claim to know much about implementing encryption,
    avoiding memory unsafety is a pretty good idea.&lt;/p&gt;

&lt;p&gt;Matrix rolls &lt;a href=&quot;https://github.com/matrix-org/matrix-rust-sdk&quot;&gt;matrix-rust-sdk&lt;/a&gt; with their Android and iOS clients
    by making &lt;a href=&quot;https://github.com/matrix-org/matrix-rust-sdk/tree/main/bindings&quot;&gt;bindings&lt;/a&gt;. This ensures that all of
    their clients work the same way when decrypting messages or whatever people on Matrix do.
    It also means that their encryption logic is faster (probably).&lt;/p&gt;

&lt;p&gt;Note that they don&#39;t write the entire app in Rust! It&#39;s not worth it!&lt;/p&gt;

&lt;h2&gt;When you&#39;re learning Rust (or just having fun)&lt;/h2&gt;

&lt;p&gt;All of this doesn&#39;t matter when you&#39;re still learning. When you are learning, it&#39;s best to strive for the thing
    you want to do whether it&#39;s worth it. Tackling things that aren&#39;t easy will teach you way more
    lessons than if you write 50 CLI calculators.&lt;/p&gt;

&lt;h2&gt;When you really want to use &lt;em&gt;that&lt;/em&gt; library&lt;/h2&gt;

&lt;p&gt;Sometimes it&#39;s worth it, just for that one library that someone made well.
    What I would recommend doing in this case is to make bindings to a language that&#39;s more appropriate for your use case.
    This is a good learning exercise too!&lt;/p&gt;

&lt;h2&gt;When not to use Rust:&lt;/h2&gt;

&lt;h3&gt;A CRUD web app&lt;/h3&gt;

&lt;p&gt;This one is very painful to me because I did this. It&#39;s really, really not worth the hassle.
    Use Laravel. It&#39;s perfectly engineered for producing shiny CRUD apps.&lt;/p&gt;

&lt;p&gt;If you really care about how performant your site is going to be, maybe consider using Rust.&lt;/p&gt;

&lt;h3&gt;Your personal website&lt;/h3&gt;

&lt;p&gt;I made this mistake! It was fun to make at first, and I enjoyed applying my knowledge, but it became such a burden
    to maintain.&lt;/p&gt;

&lt;p&gt;Axum puts way more overhead on your program than something simple like PHP&#39;s AltoRouter.
    You have to be a real HTTP server and deal with all that comes with being a real HTTP server.
    Another thing is that Axum requires you to get all of your state via specifying them in handlers,
    and then you have to worry about lifetimes and stuff. Maybe I&#39;m nitpicking, but it&#39;s too much for a small project.&lt;/p&gt;

&lt;p&gt;You will end up importing a lot of crates.
    Rust doesn&#39;t come with a lot to help you write a blog!
    I ended up with about 20 dependencies for my old blog site.&lt;/p&gt;

&lt;p&gt;Your iteration time will slow because of inflated build times.
    This one sucks if you&#39;re writing a blog!
    You have to recompile it every time (unless you&#39;re smart about it!)
    This gets worse the more you add to your site, punishing you for pouring more care into your website.&lt;/p&gt;

&lt;h3&gt;A GUI app&lt;/h3&gt;

&lt;p&gt;Overplayed, but Rust GUI sucks.&lt;/p&gt;

&lt;p&gt;If you&#39;re making a small program, you can get away with &lt;code&gt;egui&lt;/code&gt; + &lt;code&gt;eframe&lt;/code&gt; for a UI. It&#39;s pretty well-made for this use case.
    If you&#39;re going to make a larger program, make bindings from a backend to a frontend
    in a more appropriate language (Swift, Kotlin, C/++/#) or just don&#39;t use Rust.
    Consider why Rust would &lt;em&gt;help&lt;/em&gt; you with your program, and if that outweighs the technical debt of gluing a Rust blob to your program.&lt;/p&gt;

&lt;p&gt;If you&#39;re insane enough (&lt;em&gt;or well funded enough&lt;/em&gt;), you can get away with making your own GUI. Good luck!&lt;/p&gt;

&lt;h2&gt;A conclusion&lt;/h2&gt;

&lt;p&gt;These are just some recommendations, from doing most of this stuff myself.
    Rust is a fun language, I enjoy it a lot, but I can get really burnt out when I end up using it wrong.&lt;/p&gt;

&lt;p&gt;You can do anything you like, and I recommend it, but please be kind to yourself.
    If you find yourself knee-deep in a Rust program and thinking &quot;I am &lt;em&gt;really&lt;/em&gt; not enjoying myself&quot;, maybe try using something else.
    Keep doing things and having fun!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Color Schemes</title>
    <link href="https://j0.lol/blog/color-schemes/" />
    <updated>2024-09-25T12:00:00Z</updated>
    <id>https://j0.lol/blog/color-schemes/</id>
    <content type="html">&lt;p&gt;Let&#39;s talk about color schemes on my website!&lt;/p&gt;

&lt;h2&gt;The CSS&lt;/h2&gt;

&lt;p&gt;
    If you haven&#39;t noticed, I&#39;m using Tailwind colors. Yes, that Tailwind. Why?
    I like having a clear palette of colors, which is the one thing that
    forsaken ECMAScript library does right.
&lt;/p&gt;

&lt;p&gt;If you look at my CSS (I don&#39;t minimize it!) you will see at the start:&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;:&lt;/a-p&gt;&lt;a-at&gt;root&lt;/a-at&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-50&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;247&lt;/a-n&gt; &lt;a-n&gt;244&lt;/a-n&gt; &lt;a-n&gt;254&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-100&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;239&lt;/a-n&gt; &lt;a-n&gt;235&lt;/a-n&gt; &lt;a-n&gt;252&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-200&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;227&lt;/a-n&gt; &lt;a-n&gt;218&lt;/a-n&gt; &lt;a-n&gt;250&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-300&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;206&lt;/a-n&gt; &lt;a-n&gt;189&lt;/a-n&gt; &lt;a-n&gt;245&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-400&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;180&lt;/a-n&gt; &lt;a-n&gt;151&lt;/a-n&gt; &lt;a-n&gt;238&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-500&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;155&lt;/a-n&gt; &lt;a-n&gt;109&lt;/a-n&gt; &lt;a-n&gt;229&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-600&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;140&lt;/a-n&gt; &lt;a-n&gt;77&lt;/a-n&gt; &lt;a-n&gt;218&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-700&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;124&lt;/a-n&gt; &lt;a-n&gt;59&lt;/a-n&gt; &lt;a-n&gt;198&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-800&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;104&lt;/a-n&gt; &lt;a-n&gt;49&lt;/a-n&gt; &lt;a-n&gt;166&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-900&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;87&lt;/a-n&gt; &lt;a-n&gt;42&lt;/a-n&gt; &lt;a-n&gt;136&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--dull-lavender-950&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;54&lt;/a-n&gt; &lt;a-n&gt;25&lt;/a-n&gt; &lt;a-n&gt;92&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;

    &lt;a-pr&gt;--steel-gray-50&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;243&lt;/a-n&gt; &lt;a-n&gt;243&lt;/a-n&gt; &lt;a-n&gt;250&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-100&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;233&lt;/a-n&gt; &lt;a-n&gt;234&lt;/a-n&gt; &lt;a-n&gt;246&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-200&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;214&lt;/a-n&gt; &lt;a-n&gt;214&lt;/a-n&gt; &lt;a-n&gt;239&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-300&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;189&lt;/a-n&gt; &lt;a-n&gt;189&lt;/a-n&gt; &lt;a-n&gt;228&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-400&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;167&lt;/a-n&gt; &lt;a-n&gt;162&lt;/a-n&gt; &lt;a-n&gt;215&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-500&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;148&lt;/a-n&gt; &lt;a-n&gt;138&lt;/a-n&gt; &lt;a-n&gt;202&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-600&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;131&lt;/a-n&gt; &lt;a-n&gt;114&lt;/a-n&gt; &lt;a-n&gt;185&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-700&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;113&lt;/a-n&gt; &lt;a-n&gt;96&lt;/a-n&gt; &lt;a-n&gt;162&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-800&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;93&lt;/a-n&gt; &lt;a-n&gt;80&lt;/a-n&gt; &lt;a-n&gt;131&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-900&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;77&lt;/a-n&gt; &lt;a-n&gt;69&lt;/a-n&gt; &lt;a-n&gt;106&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-925&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;55&lt;/a-n&gt; &lt;a-n&gt;49&lt;/a-n&gt; &lt;a-n&gt;76&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-950&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;34&lt;/a-n&gt; &lt;a-n&gt;30&lt;/a-n&gt; &lt;a-n&gt;46&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
    &lt;a-pr&gt;--steel-gray-975&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-n&gt;17&lt;/a-n&gt; &lt;a-n&gt;15&lt;/a-n&gt; &lt;a-n&gt;23&lt;/a-n&gt;&lt;a-p&gt;;&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;
    These aren&#39;t the color schemes that come with Tailwind, just the system that
    Tailwind uses for it&#39;s color schemes. And I&#39;m not really sure where the
    actual code came from (probably some SaaS site that went pay-to-win) but I
    think it&#39;s a pretty decent way to pick colors for your website.
&lt;/p&gt;

&lt;h2&gt;How to use this&lt;/h2&gt;

&lt;p&gt;
    In your real CSS, you don&#39;t want to be using these wordy variables. You
    should &lt;em&gt;abstract&lt;/em&gt; over them. Here&#39;s a great way to do that:

    
		&lt;/p&gt;&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;:&lt;/a-p&gt;&lt;a-at&gt;root&lt;/a-at&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-c&gt;/* You need this here! View the MDN page for `light-dark()` if you&amp;#39;re curious. */&lt;/a-c&gt;

    &lt;a-pr&gt;color-scheme&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; light dark&lt;a-p&gt;;&lt;/a-p&gt;

    &lt;a-pr&gt;--bg&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-f&gt;light-dark&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;
        &lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;var&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;--dull-lavender-200&lt;/a-v&gt;&lt;a-p&gt;)),&lt;/a-p&gt; &lt;a-c&gt;/* Light */&lt;/a-c&gt;
        &lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;var&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;--steel-gray-950&lt;/a-v&gt;&lt;a-p&gt;))&lt;/a-p&gt;     &lt;a-c&gt;/* Dark  */&lt;/a-c&gt;
    &lt;a-p&gt;);&lt;/a-p&gt;


    &lt;a-pr&gt;--text&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-f&gt;light-dark&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;
        &lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;var&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;--steel-gray-900&lt;/a-v&gt;&lt;a-p&gt;)),&lt;/a-p&gt;
        &lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;var&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;--dull-lavender-200&lt;/a-v&gt;&lt;a-p&gt;))&lt;/a-p&gt;
    &lt;a-p&gt;);&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;This lets you pick your light and dark colors together!
Much better than a media query.
Here&#39;s some good design tips if you&#39;re doing that: &lt;a href=&quot;https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#design&quot;&gt;CSS Tricks&lt;/a&gt;

&lt;/p&gt;&lt;h3&gt;Make sure...&lt;/h3&gt;

&lt;ul&gt;
    &lt;li&gt;Make sure you use the &lt;code&gt;rgb()&lt;/code&gt; representation, not the hex (or use &lt;code&gt;hsl&lt;/code&gt;).
      And leave out the &lt;code&gt;rgb()&lt;/code&gt;! This lets you add transparency like this:&lt;/li&gt;
&lt;/ul&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;css&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;.&lt;/a-p&gt;&lt;a-pr&gt;whatever&lt;/a-pr&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-pr&gt;color&lt;/a-pr&gt;&lt;a-p&gt;:&lt;/a-p&gt; &lt;a-f&gt;rgb&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-f&gt;var&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-v&gt;--steel-gray-50&lt;/a-v&gt;&lt;a-p&gt;)&lt;/a-p&gt; &lt;a-o&gt;/&lt;/a-o&gt; &lt;a-n&gt;50&lt;/a-n&gt;&lt;a-t&gt;%&lt;/a-t&gt;&lt;a-p&gt;);&lt;/a-p&gt;
&lt;a-p&gt;}&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;It&#39;s a little weird to look at, but it&#39;s better than having to make &lt;code&gt;--steel-gray-50-transparent&lt;/code&gt;.&lt;/p&gt;


		&lt;aside class=&quot;speech-box&quot;&gt;
			&lt;div class=&quot;speech-character character-undefined&quot;&gt;
				&lt;div class=&quot;glass glassleft&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassleft&quot;&gt;&lt;/div&gt;

				&lt;div class=&quot;glass glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;eye glassright&quot;&gt;&lt;/div&gt;
				&lt;div class=&quot;nose&quot;&gt;&lt;/div&gt;
			&lt;/div&gt;
			&lt;p class=&quot;speech-content&quot;&gt;
&lt;strong&gt;Update:&lt;/strong&gt; You can do this while defining your variables to be full &lt;code&gt;rgb()&lt;/code&gt; colors if you use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Colors/Using_relative_colors&quot;&gt;relative color syntax&lt;/a&gt; like this: &lt;br&gt;&lt;code&gt;rgb(from var(--foo) r g b / 50%);&lt;/code&gt;
&lt;/p&gt;
		&lt;/aside&gt;

&lt;h2&gt;The tool to do this for you&lt;/h2&gt;

&lt;p&gt;
    There&#39;s &lt;a href=&quot;https://lmgt.org/?q=%22ta%E2%85%B0Iwind%22+color+palette+generator&quot;&gt;hundreds of tools out there&lt;/a&gt; to make configs for you (&lt;a href=&quot;https://www.tailwindshades.com&quot;&gt;here&#39;s my favorite&lt;/a&gt;).
    It doesn&#39;t really matter which one you pick, as long as it outputs hex codes.

    Here&#39;s some glue to convert between Tailwind configs to CSS
    (just in case you don&#39;t have some weird Emacs keybind to do this for you):
&lt;/p&gt;

&lt;style&gt;
.inputs {
    background-color: var(--plain-bg);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 0.2em;
    border-radius: 0.2em;
    font-family: Cascadia Code, monospace;
}

.inputs-surface0 {
    background-color: var(--bg-surface0);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 0.2em;
    border-radius: 0.2em;
    font-family: Cascadia Code, monospace;
}

&lt;/style&gt;
&lt;label&gt; Color name: &lt;input class=&quot;inputs&quot; id=&quot;color_name&quot; placeholder=&quot;purple&quot;&gt;&lt;/label&gt; &lt;br&gt;&lt;br&gt;
&lt;label&gt;
Your Tailwind Config: &lt;br&gt;
&lt;textarea class=&quot;inputs&quot; id=&quot;tailwind&quot; rows=&quot;13&quot; cols=&quot;40&quot; placeholder=&quot;50: &#39;#E5D9F2&#39;,
100: &#39;#DACAED&#39;,
200: &#39;#C5ABE2&#39;,
300: &#39;#B08DD8&#39;,
400: &#39;#9B6FCE&#39;,
500: &#39;#7E45BF&#39;,
600: &#39;#633498&#39;,
700: &#39;#48266E&#39;,
800: &#39;#2C1744&#39;,
900: &#39;#11091B&#39;,
950: &#39;#040206&#39;&quot;&gt;&lt;/textarea&gt;
&lt;/label&gt;

&lt;br&gt;


&lt;select id=&quot;color-format&quot; class=&quot;inputs-surface0&quot;&gt;
    &lt;option value=&quot;rgb&quot;&gt;r g b&lt;/option&gt;
    &lt;option value=&quot;hsl&quot;&gt;h s l&lt;/option&gt;
&lt;/select&gt;

&lt;p&gt;Please make sure you format it like the example above. You can leave whitespace or quotes or whatever.
&lt;/p&gt;


&lt;script&gt;
    function cssConvert() {
		const textarea = document.querySelector(&quot;textarea#tailwind&quot;);
        let palette = textarea.value || textarea.placeholder;

        let palette_name = (document.querySelector(&quot;input#color_name&quot;).value || &quot;purple&quot;).replaceAll(&quot; &quot;, &quot;-&quot;);
        let color_format = document.querySelector(&quot;select#color-format&quot;).value || &quot;rgb&quot;;

        let shades = {};

        try {
            palette.trim().split(&quot;&#92;n&quot;).forEach((i) =&gt; {
                let text = i.split(&quot;:&quot;);
                let shade_number = text[0].trim().replaceAll(&quot;&#92;&quot;&quot;, &quot;&quot;).replaceAll(&quot;&#39;&quot;, &quot;&quot;);
                let color_unparsed = text[1].trim().replaceAll(&quot;,&quot;, &quot;&quot;).replaceAll(&quot;&#92;&quot;&quot;, &quot;&quot;).replaceAll(&quot;&#39;&quot;, &quot;&quot;);

                if (color_format === &quot;rgb&quot;) {
                    shades[shade_number] = hex2rgb(color_unparsed);
                } else if (color_format === &quot;hsl&quot;) {
                    shades[shade_number] = hex2hsl(color_unparsed);
                }
            });
        } catch (error) {
            console.error(error);
            alert(error);
        }

        let str = &quot;&quot;;
        str += &quot;:root {&#92;n&quot;;

        for (const [key, value] of Object.entries(shades)) {
            str += `    --${palette_name}-${key}: ${value};&#92;n`;
        }

        str += &quot;}&quot;;

        textarea.value = str;
    }

    function hex2rgb(hex) {
        let pattern_color = &quot;^#([A-Fa-f0-9]{6})$&quot;;
        if (hex.match(pattern_color)) {
            let hex_color = hex.replace(&quot;#&quot;, &quot;&quot;)
                , r = parseInt(hex_color.substring(0, 2), 16)
                , g = parseInt(hex_color.substring(2, 4), 16)
                , b = parseInt(hex_color.substring(4, 6), 16);
            return `${r} ${g} ${b}`;
        }
        else {
            throw new Error(&#39;Error Color Format&#39;);
        }
    }

    function hex2hsl(hex) {
      const result = /^#?([a-f&#92;d]{2})([a-f&#92;d]{2})([a-f&#92;d]{2})$/i.exec(hex);

      if (!result) {
        throw new Error(&quot;Could not parse Hex Color&quot;);
      }

      const rHex = parseInt(result[1], 16);
      const gHex = parseInt(result[2], 16);
      const bHex = parseInt(result[3], 16);

      const r = rHex / 255;
      const g = gHex / 255;
      const b = bHex / 255;

      const max = Math.max(r, g, b);
      const min = Math.min(r, g, b);

      let h = (max + min) / 2;
      let s = h;
      let l = h;

      if (max === min) {
        return { h: 0, s: 0, l };
      }

      const d = max - min;
      s = l &gt; 0.5 ? d / (2 - max - min) : d / (max + min);
      switch (max) {
        case r:
          h = (g - b) / d + (g &lt; b ? 6 : 0);
          break;
        case g:
          h = (b - r) / d + 2;
          break;
        case b:
          h = (r - g) / d + 4;
          break;
      }
      h /= 6;

      s = s * 100;
      s = Math.round(s);
      l = l * 100;
      l = Math.round(l);
      h = Math.round(360 * h);

      return `${h} ${s} ${l}`;
    }
&lt;/script&gt;

&lt;br&gt;


&lt;button type=&quot;button&quot; onclick=&quot;cssConvert()&quot; class=&quot;inputs-surface0&quot;&gt;Convert this to CSS!&lt;/button&gt;
&lt;span style=&quot;font-size: 0.8em&quot;&gt;This button will work on the placeholder, if you wanna play around.&lt;/span&gt;

&lt;p&gt;&lt;strong&gt;Attributions&lt;/strong&gt; &lt;br&gt;
    Hex to HSL Source:
    &lt;br&gt;
    &lt;a href=&quot;https://www.jameslmilner.com/posts/converting-rgb-hex-hsl-colors&quot;&gt;https://www.jameslmilner.com/posts/converting-rgb-hex-hsl-colors&lt;/a&gt;
    &lt;br&gt;

    Hex to RGB Source:
    &lt;br&gt;

    &lt;a href=&quot;https://codepen.io/othmanDes/pen/VWRLrP&quot;&gt;https://codepen.io/othmanDes/pen/VWRLrP&lt;/a&gt;&lt;/p&gt;

</content>
  </entry>
  <entry>
    <title>Site remake</title>
    <link href="https://j0.lol/blog/php-site/" />
    <updated>2024-09-25T10:00:00Z</updated>
    <id>https://j0.lol/blog/php-site/</id>
    <content type="html">&lt;p&gt;I remade my website! I went from using Rust to PHP!&lt;/p&gt;

&lt;h2&gt;Why? Don&#39;t you love Rust?&lt;/h2&gt;

&lt;p&gt;I love Rust! I don&#39;t love using the wrong tool for the job though! I think that PHP is the perfect language for a &lt;strong&gt;P&lt;/strong&gt;ersonal &lt;strong&gt;H&lt;/strong&gt;ome &lt;strong&gt;P&lt;/strong&gt;age.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;It&#39;s interpreted, so I don&#39;t have to recompile the thing every time I want to change something&lt;/li&gt;
    &lt;li&gt;PHP is quick and easy, and comes with great templating built in.

        &lt;ul&gt;
            &lt;li&gt;In Rust, I had to import &lt;code&gt;minijinja&lt;/code&gt; to do this&lt;/li&gt;
        &lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Rust web development is dependency hell!&lt;/h2&gt;

&lt;p&gt;Dependencies in Rust suck. They add to your compile time, mess up your &lt;code&gt;Cargo.toml&lt;/code&gt;, and if they have macros they&#39;re even worse for compile time!&lt;/p&gt;

&lt;p&gt;Look at all the dependencies I used to make this site!&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;toml&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-p&gt;[&lt;/a-p&gt;&lt;a-pr&gt;dependencies&lt;/a-pr&gt;&lt;a-p&gt;]&lt;/a-p&gt;
&lt;a-pr&gt;ansi-to-html&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.2.1&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;axum&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.7.4&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;chrono&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.4.35&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;comrak&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.21.0&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;erased-serde&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.4.4&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;include_dir&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.7.3&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;itertools&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.12.1&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;kdl&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;4.6.0&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;knuffel&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;3.2.0&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;miette&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;version&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;7.2.0&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fancy&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-pr&gt;minijinja&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;version&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;1.0.16&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;loader&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-pr&gt;rand&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.8.5&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;select&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.6.0&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;serde&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;version&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;1.0.197&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;derive&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-pr&gt;syntect&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;5.2.0&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;thiserror&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;1.0.58&amp;quot;&lt;/a-s&gt;
&lt;a-pr&gt;tokio&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;version&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;1&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;full&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-pr&gt;tower-http&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;{&lt;/a-p&gt; &lt;a-pr&gt;version&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.5.2&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;,&lt;/a-p&gt; &lt;a-pr&gt;features&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-p&gt;[&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;fs&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;]&lt;/a-p&gt; &lt;a-p&gt;}&lt;/a-p&gt;
&lt;a-pr&gt;tracing-subscriber&lt;/a-pr&gt; &lt;a-o&gt;=&lt;/a-o&gt; &lt;a-s&gt;&amp;quot;0.3.18&amp;quot;&lt;/a-s&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;How many dependencies do I have in PHP?&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;json&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;{
    &lt;a-s&gt;&amp;quot;require&amp;quot;&lt;/a-s&gt;: {
        &lt;a-s&gt;&amp;quot;altorouter/altorouter&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;^2.0&amp;quot;&lt;/a-s&gt;,
        &lt;a-s&gt;&amp;quot;michelf/php-markdown&amp;quot;&lt;/a-s&gt;: &lt;a-s&gt;&amp;quot;^2.0&amp;quot;&lt;/a-s&gt;
    }
}
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;Two! I could probably get it even lower if I were to use my web server&#39;s routing, but ehhhhhhhhhh&lt;/p&gt;

&lt;p&gt;Altorouter is a pretty nice to use router, and PHP Markdown renders my blog&#39;s posts.&lt;/p&gt;

&lt;h2&gt;Okay, but how do you do syntax highlighting?&lt;/h2&gt;

&lt;p&gt;PHP-Markdown doesn&#39;t come with it! We need to shoehorn it in ourselves.&lt;/p&gt;

&lt;p&gt;After looking around, I found &lt;code&gt;Shiki-PHP&lt;/code&gt;, which offers code highlighting by making bindings to &lt;code&gt;Shiki&lt;/code&gt;: &lt;a href=&quot;https://github.com/spatie/shiki-php&quot;&gt;https://github.com/spatie/shiki-php&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;Shiki&lt;/code&gt; is slow. Very slow. It uses &lt;em&gt;Node JS&lt;/em&gt;. As we both know, running JavaScript on the server is the worst thing you can do for your poor program.&lt;/p&gt;

&lt;p&gt;I used &lt;code&gt;Syntect&lt;/code&gt; before to do highlighting in Rust. It&#39;s very good! Just one issue: It&#39;s in Rust!&lt;/p&gt;

&lt;h3&gt;Let&#39;s use Rust in PHP!&lt;/h3&gt;

&lt;p&gt;Using &lt;a href=&quot;https://github.com/davidcole1340/ext-php-rs&quot;&gt;ext-php-rs&lt;/a&gt;, all I need to do is to make a stub program to hook &lt;code&gt;Syntect&lt;/code&gt; in, then install it as a PHP Module.&lt;/p&gt;

&lt;p&gt;I need two functions:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;One, to turn code into HTML&lt;/li&gt;
    &lt;li&gt;Another, to generate CSS for the highlighting&lt;/li&gt;
&lt;/ul&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;php&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;#[php_function]
pub fn syntect_highlight(code: &amp;amp;str, language_ext: &amp;amp;str) -&amp;gt; String {
    let syntax_set = SyntaxSet::load_defaults_newlines();
    let syntax = syntax_set.find_syntax_by_extension(language_ext).unwrap();
    let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &amp;amp;syntax_set, ClassStyle::Spaced);
    for line in LinesWithEndings::from(code) {
        html_generator.parse_html_for_line_which_includes_newline(line).unwrap();
    }
    let output_html= html_generator.finalize();
    output_html
}

#[php_function]
pub fn syntect_css(theme_path: &amp;amp;str) -&amp;gt; String {
    let theme = ThemeSet::get_theme(PathBuf::from(theme_path)).unwrap();
    css_for_theme_with_class_style(&amp;amp;theme, ClassStyle::Spaced).unwrap()
}
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;Now, we can just build this with &lt;code&gt;cargo build&lt;/code&gt;, and uh. Get a &lt;code&gt;.dylib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our glue library &lt;code&gt;ext-php-rs&lt;/code&gt; offers a solution to install this all for us, but it doesn&#39;t work on my machine! And it seems abandoned!
    Instead, let&#39;s just install it ourselves.&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;I got this working on my Ubuntu Server! Not my MacOS laptop though. I had to build it from git instead of the latest version though.&lt;/p&gt;

    &lt;p&gt;&lt;code&gt;cargo install cargo-php --git https://github.com/davidcole1340/ext-php-rs&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;If you have PHP installed globally you will have to install it on the super-user (use sudo).
        Overall this seems spotty, and I&#39;m looking for an alternative to this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From &lt;strong&gt;&lt;code&gt;cargo-php&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;rust&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-k&gt;fn&lt;/a-k&gt; get_ext_dir&lt;a-p&gt;()&lt;/a-p&gt; -&amp;gt; &lt;a-t&gt;AResult&lt;/a-t&gt;&lt;a-p&gt;&amp;lt;&lt;/a-p&gt;&lt;a-t&gt;PathBuf&lt;/a-t&gt;&lt;a-p&gt;&amp;gt;&lt;/a-p&gt; &lt;a-p&gt;{&lt;/a-p&gt;
    &lt;a-k&gt;let&lt;/a-k&gt; cmd = &lt;a-t&gt;Command&lt;/a-t&gt;&lt;a-p&gt;::&lt;/a-p&gt;&lt;a-f&gt;new&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;php&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;arg&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;-r&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;arg&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;echo ini_get(&amp;#39;extension_dir&amp;#39;);&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;output&lt;/a-f&gt;&lt;a-p&gt;()&lt;/a-p&gt;
        &lt;a-p&gt;.&lt;/a-p&gt;&lt;a-f&gt;context&lt;/a-f&gt;&lt;a-p&gt;(&lt;/a-p&gt;&lt;a-s&gt;&amp;quot;Failed to call PHP&amp;quot;&lt;/a-s&gt;&lt;a-p&gt;)&lt;/a-p&gt;?&lt;a-p&gt;;&lt;/a-p&gt;
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;Okay, so...&lt;/p&gt;


		&lt;figure class=&quot;code-block&quot;&gt;
			&lt;figcaption&gt;
					&lt;span class=&quot;lang code-lang-tag&quot;&gt;sh&lt;/span&gt;
					
				&lt;/figcaption&gt;
			&lt;pre&gt;&lt;code&gt;&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-f&gt; &lt;/a-f&gt;&lt;a-pr&gt;php&lt;/a-pr&gt; &lt;a-co&gt;-r&lt;/a-co&gt; &lt;a-s&gt;&amp;quot;echo ini_get(&amp;#39;extension_dir&amp;#39;);&amp;quot;&lt;/a-s&gt;

&lt;a-f&gt;/opt/homebrew/lib/php/pecl/20230831&lt;/a-f&gt;

&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-f&gt; #&lt;/a-f&gt; Please ignore how i blatantly misspelled syntect here. Shame on me.
&lt;a-o&gt;$&lt;/a-o&gt;&lt;a-f&gt; &lt;/a-f&gt;&lt;a-pr&gt;cp&lt;/a-pr&gt; ./target/release/libsyntext_ext_php.dylib /opt/homebrew/lib/php/pecl/20230831
&lt;/code&gt;&lt;/pre&gt;
		&lt;/figure&gt;

&lt;p&gt;Okay, now we need to enable it in &lt;code&gt;php.ini&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;code-block&quot;&gt;
	&lt;figcaption&gt;
		&lt;span class=&quot;filename code-filename-tag&quot;&gt;.../php/8.3/php.ini&lt;/span&gt;
	&lt;/figcaption&gt;
	&lt;pre&gt;&lt;code&gt;extension=...
&lt;a-da&gt;extension=libsyntext_ext_php.dylib&lt;/a-da&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;Now we can use it in PHP. Awesome! The code blocks on this site are being rendered like this :)&lt;/p&gt;

&lt;p&gt;Note that this technically adds a third dependency, if you were keeping track. Better than 19!!&lt;/p&gt;

&lt;p&gt;Also: if you want to use it, here&#39;s the repo: &lt;a href=&quot;https://github.com/j0lol/ext-php-syntect&quot;&gt;https://github.com/j0lol/ext-php-syntect&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>ZeroBridge 0.1.0 Release</title>
    <link href="https://j0.lol/blog/zerobridge-launch/" />
    <updated>2024-04-03T10:00:00Z</updated>
    <id>https://j0.lol/blog/zerobridge-launch/</id>
    <content type="html">&lt;p&gt;
    I wrote a Discord bridge for Minecraft 1.4.7! I might go in depth about this
    in another blog post, but for now, here&#39;s a link:
    &lt;a href=&quot;https://git.gay/j0/ZeroBridge&quot;&gt;ZeroBridge&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;Okay here&#39;s a bit of talk&lt;/h2&gt;

&lt;p&gt;
    It runs in NilLoader, and is mainly targeted for the pack Rewind Upsilon. It
    was really fun to make! NilLoader is very barebones, and most of the code is
    directly injected into Minecraft via MiniTransformer. For some reason, I
    find working with ASM (Java Bytecode Manipulator) to be quite nice.
&lt;/p&gt;

&lt;p&gt;
    I originally started work on this project maybe a year ago? I digged it out
    recently from my backups because I wanted to play Upsilon but the people I
    want to play with &lt;em&gt;really&lt;/em&gt; want a Discord bridge. I basically rewrote
    the entire thing to play nice when inside Rewind Upsilon. I also added a
    config (with Jankson), webhooks (with Visage rendering), a bunch of other
    stuff that makes it feel like a real thing.
&lt;/p&gt;

&lt;p&gt;Anyway, enjoy!&lt;/p&gt;

&lt;p&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/zerobridge-launch/m4izdBZYhv-610.webp 610w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/zerobridge-launch/m4izdBZYhv-610.jpeg&quot; alt=&quot;ZeroBridge being used on Discord&quot; width=&quot;610&quot; height=&quot;642&quot;&gt;&lt;/picture&gt;
&lt;/p&gt;

&lt;p&gt;
    &lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://j0.lol/blog/zerobridge-launch/C0Ewtdhl_f-922.webp 922w&quot;&gt;&lt;img src=&quot;https://j0.lol/blog/zerobridge-launch/C0Ewtdhl_f-922.jpeg&quot; alt=&quot;ZeroBridge being used on Minecraft&quot; width=&quot;922&quot; height=&quot;576&quot;&gt;&lt;/picture&gt;
&lt;/p&gt;
</content>
  </entry>
</feed>