<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: stereobooster</title>
    <description>The latest articles on DEV Community by stereobooster (@stereobooster).</description>
    <link>https://dev.to/stereobooster</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F94664%2Fdd5f88a0-3b21-4a84-8dac-95a1f8d1fbb5.png</url>
      <title>DEV Community: stereobooster</title>
      <link>https://dev.to/stereobooster</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stereobooster"/>
    <language>en</language>
    <item>
      <title>Run Cystoscape.js with Node.js</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Thu, 12 Oct 2023 16:33:48 +0000</pubDate>
      <link>https://dev.to/stereobooster/run-cystoscapejs-with-nodejs-4g02</link>
      <guid>https://dev.to/stereobooster/run-cystoscapejs-with-nodejs-4g02</guid>
      <description>&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; If you need to generate an image on the server side.&lt;/p&gt;

&lt;p&gt;At first, I thought it was impossible. There is &lt;a href="https://github.com/cytoscape/cytosnap"&gt;cytosnap&lt;/a&gt; that uses Puppeteer (headless browser). Then, for fun, &lt;a href="https://stereobooster.com/content/posts/tauri-instead-of-puppeteer-or-playwright/index.md"&gt;I rewrote it with Tauri&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But later, I though, what if it would be possible to run it with Node.js. And, indeed, it’s possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Add polyfils for the browser (it also includes canvas):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;XMLSerializer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;serializeToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;XMLSerializer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;XMLSerializer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;JSDOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;div id="cy"&amp;gt;&amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicitly set bounding box&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a graph the same way you would in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#cy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cytoscape&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now I was able to render it only as SVG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to not polute global scope with polyfills we can run this script as sub-process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;executablePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`bin/cyto-nodejs.js`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;windowsHide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;Source code is here: &lt;a href="https://github.com/stereobooster/cyto-nodejs"&gt;https://github.com/stereobooster/cyto-nodejs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cytoscape</category>
      <category>webdev</category>
      <category>diagram</category>
      <category>cli</category>
    </item>
    <item>
      <title>Hugo ideal image</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Tue, 10 Oct 2023 23:14:33 +0000</pubDate>
      <link>https://dev.to/stereobooster/hugo-ideal-image-4blj</link>
      <guid>https://dev.to/stereobooster/hugo-ideal-image-4blj</guid>
      <description>&lt;p&gt;I created image components a couple of times before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/stereobooster/responsive-images-for-hugo-dn9"&gt;Responsive images for Hugo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stereobooster/react-ideal-image"&gt;react-ideal-image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But browsers keep improving - now almost all modern browsers support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/loading-lazy-attr"&gt;loading=lazy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/srcset"&gt;srcset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/picture"&gt;picture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caniuse.com/webp"&gt;webp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I checked existing solutions - they either don’t do what I want or are complicated (for my taste):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hugomods.com/en/docs/images/"&gt;Hugo Images Module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hugo-mods/lazyimg"&gt;lazyimg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/danielfdickinson/image-handling-mod-hugo-dfd"&gt;DFD Hugo image handling module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;What I expect from an “ideal” image:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Responsiveness&lt;/li&gt;
&lt;li&gt;Lazy-loading&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/transitive-bullshit/lqip-modern"&gt;Modern LQIP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stereobooster.com/posts/portable-markdown-links/"&gt;Portable markdown links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Dark mode&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;There are different ways to implement image components in Hugo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/templates/partials/"&gt;partial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/templates/render-hooks/"&gt;render hook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/templates/shortcode-templates/"&gt;shortcodes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think the most pragmatic way would be to implement it as “partial” which accepts an image as a resource. This way it can be reused in render hooks and shortcodes, and the resolution of the image would be the responsibility of the caller.&lt;/p&gt;

&lt;p&gt;It can look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{ partial "picture.html" (dict "img" $img "alt" $.Text) }}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Partial &lt;code&gt;picture.html&lt;/code&gt; would be responsible for: detecting width and height, resizing the image, and rendering HTML. There are no special requirements for HTML or CSS so it would be a pretty portable solution (copy one file).&lt;/p&gt;

&lt;h3&gt;
  
  
  Responsiveness
&lt;/h3&gt;

&lt;p&gt;Component would be responsible for resizing image and rendering &lt;code&gt;srcset&lt;/code&gt; attribute. Basic code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;srcset="
{{- (.Resize "330x").RelPermalink }} 330w,
{{- (.Resize "660x").RelPermalink }} 660w,
{{- (.Resize "1024x").RelPermalink }} 1024w,
{{- (.Resize "1320x").RelPermalink }} 2x"
src="{{ (.Resize "660x").RelPermalink }}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it suppose to resize only raster images (not SVG)&lt;/li&gt;
&lt;li&gt;it should not upscale image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally we can provide webp version of the image. Basic code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
&amp;lt;source
srcset="
{{- (.Resize "330x webp").RelPermalink }} 330w,
{{- (.Resize "660x webp").RelPermalink }} 660w,
{{- (.Resize "1024x webp").RelPermalink }} 1024w,
{{- (.Resize "1320x webp").RelPermalink }} 2x"
src="{{ (.Resize "660x webp").RelPermalink }}"
/&amp;gt;
&amp;lt;img ... /&amp;gt;
&amp;lt;/picture&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Component only responsible for HTML part. You probably would need to add some CSS, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;img {
 max-width: 100%;
 height: auto;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lazy-loading
&lt;/h3&gt;

&lt;p&gt;In order to use lazy-load it is enough to add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loading="lazy"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and optionaly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decoding="async"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it is important to set width and height of the image, to prevent reflow - browser would know ratio of the image upfront and will reserve space for it without downloading it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is pretty easy to do with Hugo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;width="{{ .Width }}" height="{{ .Height }}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it works only for raster images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For SVG we need to do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{ if (eq .MediaType.SubType "svg") }}
{{ $width := ""}}
{{ $height := ""}}
{{ $svgContent := .Content }}
{{ range (findRESubmatch `&amp;lt;svg[^&amp;gt;]*width=["']([.0-9]*)["'a-zA-Z]` $svgContent 1) }}
{{ $width = index . 1 }}
{{ end }}
{{ range (findRESubmatch `&amp;lt;svg[^&amp;gt;]*height=["']([.0-9]*)["'a-zA-Z]` $svgContent 1) }}
{{ $height = index . 1 }}
{{ end }}
{{ if (eq "" $width $height) }}
{{ range (findRESubmatch `&amp;lt;svg[^&amp;gt;]*viewBox=["']?([.0-9]*) ([.0-9]*) ([.0-9]*) ([.0-9]*)` $svgContent 1) }}
{{ $width = index . 3 }}
{{ $height = index . 4 }}
{{ end }}
{{ end }}
{{ if (eq "" $width $height) }}
{{ warnf "Can't detect width and height for SVG %s" .RelPermalink }}
{{/* do not use lazy without dimensions */}}
{{ $lazy = false }}
{{ end }}
{{ end }}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code reads SVG as text and uses regular expression in order to match &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, and &lt;code&gt;viewBox&lt;/code&gt;. &lt;strong&gt;Important&lt;/strong&gt; : it should not match values with &lt;code&gt;%&lt;/code&gt;, because they are useless in this case. On the other hand - any absolute units can be matched, like &lt;code&gt;px&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LQIP
&lt;/h3&gt;

&lt;p&gt;There are many LQIP approaches. I like, so called, modern LQIP, which uses 20px, 20% quality webp image as base64.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;picture
{{ if $lqip }}
{{ $bg := (.Resize "20x webp q20").Content | base64Encode }}
style="background-image:url(data:image/webp;base64,{{ $bg }});background-size:cover"
{{ end }}&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I already introduced &lt;code&gt;picture&lt;/code&gt; in the markup, so I will reuse it as “holder” for LQIP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;for transparent images (png, webp) LQIP may be problematic, so there suppose to be a way to disable it&lt;/li&gt;
&lt;li&gt;I don’t want to generate LQIP for SVG, because I would need to rasterize SVG first, which is too much trouble&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionaly you may want to add CSS in order to make picture of the same size as image, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;picture {
 display: block;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To improve appearance it is recomended to add blur:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;picture &amp;gt; img {
 -webkit-backdrop-filter: blur(10px);
 backdrop-filter: blur(10px);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Image is on “top” of picture. While it is loading it will be transparent. So we can use it add blur to the background. &lt;strong&gt;Important&lt;/strong&gt; : this trick doesn’t work in Firefox, see &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1556156"&gt;Bug 1556156&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portable markdown links
&lt;/h3&gt;

&lt;p&gt;This one is easy. Partial take resource rather than path. So it is responsibility of caller to resolve path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dark mode
&lt;/h3&gt;

&lt;p&gt;I’m not sure yet how to implement “dark mode” for images. From an HTML point of view it is trivial to implement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
 &amp;lt;source media="(prefers-color-scheme: dark)" srcset="..." /&amp;gt;
 &amp;lt;source media="(prefers-color-scheme: light)" srcset="..." /&amp;gt;
 &amp;lt;img src="..." /&amp;gt;
&amp;lt;/picture&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how to do it from Markdown’s point of view? Some options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;convention&lt;/strong&gt;. If there is an image &lt;code&gt;img.png&lt;/code&gt; and &lt;code&gt;img_dark.png&lt;/code&gt;, the system can automatically find the second image and use it for dark mode&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;shortcode&lt;/strong&gt;. Will work, but this is not a markdown solution&lt;/li&gt;
&lt;li&gt;Use some kind of &lt;strong&gt;marker in the url&lt;/strong&gt; to distinguis dark-/light-mode images. For example, &lt;code&gt;#gh-dark-mode-only&lt;/code&gt; or &lt;code&gt;#gh-light-mode-only&lt;/code&gt;. See &lt;a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#specifying-the-theme-an-image-is-shown-to"&gt;Specifying the theme an image is shown to&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SVGs don’t need a special solution, they can handle it internally. See &lt;a href="https://stackoverflow.com/questions/67187091/creating-svg-that-appears-black-in-light-mode-and-light-in-dark-mode"&gt;Creating SVG that appears black in light mode and light in dark mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;for black and white images &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert"&gt;&lt;code&gt;filter: invert(1)&lt;/code&gt;&lt;/a&gt; would do the trick&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other
&lt;/h3&gt;

&lt;p&gt;I want to keep number of params minimal. Currently it supports only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;img&lt;/code&gt; - image as resource&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alt&lt;/code&gt; - string, alternative text&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;class&lt;/code&gt; - string, for styling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lazy&lt;/code&gt; - boolean, whether it should use lazy loading or not&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;webp&lt;/code&gt; - boolean, whether it should generate webp version or not&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lqip&lt;/code&gt; - boolean, whether it should generate LQIP or not&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;My current implementation is here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stereobooster/hugo-ideal-image/blob/main/layouts/partials/picture.html"&gt;picture partial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/stereobooster/hugo-ideal-image/blob/main/layouts/_default/_markup/render-image.html"&gt;render hook for image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hugo</category>
      <category>webdev</category>
      <category>image</category>
    </item>
    <item>
      <title>Tauri instead of Puppeteer or Playwright?</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Tue, 03 Oct 2023 11:14:09 +0000</pubDate>
      <link>https://dev.to/stereobooster/tauri-instead-of-puppeteer-or-playwright-3ic4</link>
      <guid>https://dev.to/stereobooster/tauri-instead-of-puppeteer-or-playwright-3ic4</guid>
      <description>&lt;p&gt;Tauri and Puppeteer/Playwright have different use cases. So it seems nothing to compare. But there is (at least) one use case where you may try to use Tuari instead of a headless browser - “snapshots”. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/stereobooster/react-snap"&gt;react-snap&lt;/a&gt; - HTML snapshot, uses Puppeteer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mermaid-js/mermaid-cli"&gt;mermaid-cli&lt;/a&gt; - SVG/image snapshot, uses Puppeteer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/cytoscape/cytosnap"&gt;cytosnap&lt;/a&gt; - image snapshot, uses Puppeteer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Because Tauri uses provided by OS browser and produces small binaries (let’s say ~10 MB). While Puppeteer and Playwright need a full browser (let’s say ~200 MB). And Tauri probably can be faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiment
&lt;/h2&gt;

&lt;p&gt;Let’s see if this will work in practice. For an experiment, I decided to recreate &lt;code&gt;cytosnap&lt;/code&gt; with Tauri. Functionality is trivial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read input from file or STDIN (&lt;a href="https://js.cytoscape.org/#notation/elements-json"&gt;Elements JSON&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;generate a graph with the help of Cytoscape.js&lt;/li&gt;
&lt;li&gt;write result (image from canvas) to file or STDOUT&lt;/li&gt;
&lt;li&gt;and this is supposed to be CLI, not GUI-app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CLI
&lt;/h3&gt;

&lt;p&gt;CLI applications are not a typical use case for Tauri, but it is possible.&lt;/p&gt;

&lt;p&gt;Hide window (&lt;code&gt;tauri.conf.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"windows": [{ "visible": false, ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hide icon from Dock bar (Mac OS only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(target_os&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"macos"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tauri&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ActivationPolicy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nn"&gt;tauri&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.setup&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[cfg(target_os&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"macos"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.set_activation_policy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ActivationPolicy&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Accessory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add CLI arguments (&lt;code&gt;tauri.conf.json&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "cli": {
      "description": "Render graphs on the server side with Cytoscape.js, getting image file as output",
      "args": [
        {
          "name": "source",
          "short": "s",
          "takesValue": true,
          "multiple": false,
          "multipleOccurrences": false
        },
        {
          "name": "destination",
          "short": "d",
          "takesValue": true,
          "multiple": false,
          "multipleOccurrences": false
        }
      ],
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Overcome security fences
&lt;/h3&gt;

&lt;p&gt;Because Tauri’s main use case is GUI, they care about security and limit what files/folders the application can access. Which doesn’t work for the way people use CLI. So I had to write my own Tauri commands to write and read files, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[tauri::command]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write_destination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;general_purpose&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;STANDARD&lt;/span&gt;&lt;span class="nf"&gt;.decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;absolute_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : I need to pass binary data from the front end to the rust, so I use Base64.&lt;/p&gt;

&lt;h3&gt;
  
  
  Distribution
&lt;/h3&gt;

&lt;p&gt;Initially, I wanted to distribute this CLI as &lt;a href="//content/posts/distributing-executable-binaries-in-npm/index.md"&gt;binary inside the npm package&lt;/a&gt;. But then I realized that &lt;a href="https://github.com/tauri-apps/tauri/discussions/3048"&gt;Tauri can’t really produce portable binaries&lt;/a&gt;. Tauri relies on the OS’s browser - which is a neat trick to shrink down the size of the binary, but this is what makes it less portable. Trade-offs as always.&lt;/p&gt;

&lt;p&gt;Not to stop an experiment I decided to distribute at least binary for Mac OS in npm. And it works (Mac OS only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx @stereobooster/cyto-snap -s g2.json -d g2.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Size of the npm package
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm notice package size: 2.9 MB
npm notice unpacked size: 7.4 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn’t do any Tauri optimizations, so it probably can be smaller. Also, this is Mac OS-only binary, it will be bigger if we pack binaries for all platforms in one npm package.&lt;/p&gt;

&lt;p&gt;And it has 0 dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source code
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt; : because this is an experiment, I didn’t try to make it perfect. Just made it work.&lt;/p&gt;

&lt;p&gt;Source code: &lt;a href="https://github.com/stereobooster/cyto-snap"&gt;https://github.com/stereobooster/cyto-snap&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Homebrew
&lt;/h3&gt;

&lt;p&gt;The idea of distributing binary in npm failed, so I think to try to distribute it via Homebrew. Homebrew works on Mac OS, Windows (WSL 2), and Linux (though they have their own package managers).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Produce standard desktop installers for Tauri (&lt;code&gt;tauri-apps/tauri-action&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Upload installers to GitHub releases&lt;/li&gt;
&lt;li&gt;Run installers in silent mode with the Homebrew formula&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Full automation with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;It has a lot of steps to produce the final package. I started automation but didn’t finish it. Ideally, it should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build application&lt;/li&gt;
&lt;li&gt;Run tests (I do integration tests with &lt;a href="https://github.com/dmtrKovalenko/odiff"&gt;odiff&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Create a tag and push it&lt;/li&gt;
&lt;li&gt;Create release and upload all binaries&lt;/li&gt;
&lt;li&gt;Update the Homebrew formula and publish it&lt;/li&gt;
&lt;li&gt;Publish the npm package (but it is not very useful without portable binaries)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;This approach may work with Homebrew distribution

&lt;ul&gt;
&lt;li&gt;It is sad that you can’t publish binaries to npm, which would make JS-developer use other means to install it (Homebrew, apt-get, cURL, etc)&lt;/li&gt;
&lt;li&gt;On the other hand, it is fully independent of npm (and Node.js ecosystem in general), so can be used by none-JS developers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;This was my first time using Tauri, and it is awesome&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tauri</category>
      <category>rust</category>
      <category>cli</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Distributing executable binaries in npm package</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sun, 01 Oct 2023 09:28:23 +0000</pubDate>
      <link>https://dev.to/stereobooster/distributing-executable-binaries-in-npm-package-1d4l</link>
      <guid>https://dev.to/stereobooster/distributing-executable-binaries-in-npm-package-1d4l</guid>
      <description>&lt;p&gt;Let’s say you need to distribute binary in npm. I’m not talking about node extensions (node-gyp, napi, etc.). I’m talking about standalone executables, like CLI applications.&lt;/p&gt;

&lt;p&gt;We have options. &lt;strong&gt;How to distribute binary&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;host executables on some server, for example, GitHub releases, and download appropriate (e.g. for given OS and architecture) binary on installation&lt;/li&gt;
&lt;li&gt;put all executables (for all OSs and architectures) in one npm package&lt;/li&gt;
&lt;li&gt;create sub-packages with binary for each OS and architecture and one “root” npm package which would list all sub-packages as optional dependencies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;How to run the binary&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use JS wrapper which would call actual executable with &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;spawn&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;On installation figure out where Node puts binaries and overwrite it with your binary from the package&lt;/li&gt;
&lt;li&gt;Use JS script to call node binding (&lt;code&gt;*.node&lt;/code&gt;) or WASM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Tools and blog posts&lt;/strong&gt; :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;distribution&lt;/th&gt;
&lt;th&gt;execution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/EverlastingBugstopper/binary-install"&gt;binary-install&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;spawnSync&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/avh4/binwrap"&gt;binwrap&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;spawn&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/mole-inc/bin-wrapper"&gt;bin-wrapper&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;spawn&lt;/code&gt;, …)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/prebuild/prebuild-install"&gt;prebuild-install&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blog.xendit.engineer/how-we-repurposed-npm-to-publish-and-distribute-our-go-binaries-for-internal-cli-23981b80911b"&gt;blog.xendit.enginee&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://hspak.dev/post/publish-npm/"&gt;hspak.dev&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/prebuild/prebuildify"&gt;prebuildify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://blog.orhun.dev/packaging-rust-for-npm/"&gt;blog.orhun.dev&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;spawnSync&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/napi-rs/package-template"&gt;napi-rs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Example projects&lt;/strong&gt; :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;distribution&lt;/th&gt;
&lt;th&gt;execution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/dmtrKovalenko/odiff"&gt;odiff&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/withastro/compiler"&gt;@astrojs/compiler&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;2 (WASM)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/evanw/esbuild"&gt;esbuild&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;execFileSync&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/biomejs/biome"&gt;biome&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;spawnSync&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/vercel/turbo/tree/main/packages/turbo"&gt;Turborepo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1 (&lt;code&gt;execFileSync&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/parcel-bundler/lightningcss"&gt;lightningcss&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>npm</category>
    </item>
    <item>
      <title>Useful modern tools for static websites</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Tue, 26 Sep 2023 15:50:02 +0000</pubDate>
      <link>https://dev.to/stereobooster/useful-modern-tools-for-static-websites-i9p</link>
      <guid>https://dev.to/stereobooster/useful-modern-tools-for-static-websites-i9p</guid>
      <description>&lt;p&gt;This list is about tools beyond &lt;a href="https://www.staticwebsitehosting.org/"&gt;static hosting&lt;/a&gt; and &lt;a href="https://jamstack.org/generators/"&gt;static website generators&lt;/a&gt;. There are already enough list about that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analytics
&lt;/h2&gt;

&lt;p&gt;In old times there was basically one default choice: Google analytics. What is bad about GA:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JS size (about 49kb). There alternative clients though, like this &lt;a href="https://github.com/lukeed/ganalytics"&gt;one&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blocked by default by some browsers and adblockers&lt;/li&gt;
&lt;li&gt;no GDPR compliance (e.g. you always need to provide cookies banner)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But there are a lot of better alternatives (just to name few):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://tinyanalytics.io/"&gt;https://tinyanalytics.io/&lt;/a&gt; - 1 site for free&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://umami.is/"&gt;https://umami.is/&lt;/a&gt; - 3 sites for free&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://swetrix.com/"&gt;https://swetrix.com/&lt;/a&gt; - open source, starts from $5/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See more alternatives here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://european-alternatives.eu/category/web-analytics-services"&gt;European web analytics services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tinyanalytics.io/google-analytics-alternatives"&gt;19 Google Analytics Alternatives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goatcounter.com/"&gt;goatcounter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also some “static hostings” provide analytics, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cloudflare.com/web-analytics/"&gt;Cloudflare Pages&lt;/a&gt; there is a free tier&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.netlify.com/products/analytics/"&gt;Netlify&lt;/a&gt; $9/month&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Search
&lt;/h2&gt;

&lt;p&gt;In old times people would use “Google custom search engine” (I can’t find it anymore, I guess, they discounted it). There is as well Algolia. But who needs third party paid solution, when you can do it for free.&lt;/p&gt;

&lt;p&gt;Recent trend is to generate search index during site generation and then use client side library to do the search. It will probably won’t scale for a huge website, but for personal blog more than enough.&lt;/p&gt;

&lt;p&gt;Some options (not a full list):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/oramasearch/orama"&gt;orama&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cloudcannon/pagefind"&gt;pagefind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/krisk/fuse"&gt;fuse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/olivernn/lunr.js"&gt;lunr.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honorable mentions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/typesense/typesense"&gt;typesense&lt;/a&gt; - open-source, but needs server&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docsearch.algolia.com/"&gt;docsearch&lt;/a&gt; - free Algolia search for developer docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or you can do something crazy, like hosting &lt;a href="https://phiresky.github.io/blog/2021/hosting-sqlite-databases-on-github-pages/"&gt;SQLite database over HTTP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comments
&lt;/h2&gt;

&lt;p&gt;In old times people would use Disqus. But again, it is not GDPR compliant out of the box and there is huge JS payload (at least it was, when I used it last time).&lt;/p&gt;

&lt;p&gt;One of potential solution for developers is to reuse github comments or discussions as comments for your website. Most developers have github account anyway (I know there are people, who are strongly opposed to Github and prefer Gitlab or self-hosted solutions, but this is discussion for another time). For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://giscus.app/"&gt;https://giscus.app/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://utteranc.es/"&gt;https://utteranc.es/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other option is not to host comments directly on your website, but put a link to a place where people can discuss (especially if you crosspost), for example: hacker news, dev.to, reddit, twitter etc.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>blog</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Runtime type validators</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sun, 24 Sep 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/runtime-type-validators-1o60</link>
      <guid>https://dev.to/stereobooster/runtime-type-validators-1o60</guid>
      <description>&lt;h2&gt;
  
  
  Where can be used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;validate http request from client (at server side)&lt;/li&gt;
&lt;li&gt;validate http response from server (at client side)&lt;/li&gt;
&lt;li&gt;validate data from untyped storage, for example localStorage, JSON columns in DB, etc.&lt;/li&gt;
&lt;li&gt;validate env variables&lt;/li&gt;
&lt;li&gt;validate CLI arguments&lt;/li&gt;
&lt;li&gt;validate configuration files&lt;/li&gt;
&lt;li&gt;validate data from URL (query params)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic validation - can validate static types at runtime (can be restricted to JSON type)&lt;/li&gt;
&lt;li&gt;Serialisation / deserialisation - for example, TS/JS supports &lt;code&gt;Date&lt;/code&gt;, but JSON doesn’t. So you need to do conversion in order to support it&lt;/li&gt;
&lt;li&gt;Compatibility with other schemas - there are tools which can convert from- or to- another schemas, for example JSONSchema, OpenAPI etc&lt;/li&gt;
&lt;li&gt;Strict or permissive mode - would object be accepted if it contains extra fields&lt;/li&gt;
&lt;li&gt;Helpful error messages - if in case of failed validation it points what exactly went wrong or simply dismisses the input&lt;/li&gt;
&lt;li&gt;AOT (ahead of time) - can it use type information in order to produce validation libraries or faster serialiser, deserialiser&lt;/li&gt;
&lt;li&gt;Custom types - can user create custom types, for example type for email, instead of string&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Libraries
&lt;/h2&gt;

&lt;p&gt;Not a full list, but rather a high level overview&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/samchon/typia"&gt;typia&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Doesn’t need extra schema definition. Uses AOT to generate validators, serialisers and deserialisers directly from TypeScript types&lt;/li&gt;
&lt;li&gt;Supports protocol buffers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/sinclairzx81/typebox"&gt;typebox&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Supports all JSON, JS, JSONSchema, but for TS discriminated unions you need to use &lt;a href="https://github.com/jtlapp/typebox-validators"&gt;typebox-validators&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Can use JIT (and AOT?)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/GoogleFeud/ts-runtime-checks"&gt;ts-runtime-checks&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Uses AOT&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See: &lt;a href="https://moltar.github.io/typescript-runtime-type-benchmarks/"&gt;Runtype Benchmarks&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Popular
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/colinhacks/zod"&gt;zod&lt;/a&gt;. For example, integrates with &lt;a href="https://docs.astro.build/en/guides/content-collections/#defining-datatypes-with-zod"&gt;Astro&lt;/a&gt;, &lt;a href="https://orm.drizzle.team/docs/zod"&gt;Drizzle&lt;/a&gt;, &lt;a href="https://trpc.io/docs/server/validators#with-zod"&gt;trpc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jquense/yup"&gt;yup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ajv-validator/ajv"&gt;ajv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://typeschema.com/"&gt;typeschema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fabian-hiller/valibot"&gt;valibot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/adonisjs/validator"&gt;adonisjs/validator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/gcanti/io-ts"&gt;io-ts&lt;/a&gt; and &lt;a href="https://github.com/gcanti/newtype-ts"&gt;newtype-ts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.deepkit.io/english/runtime-types.html"&gt;deepkit runtime types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pelotom/runtypes"&gt;runtypes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ianstormtaylor/superstruct"&gt;superstruct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/denostack/safen"&gt;safen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/arktypeio/arktype"&gt;arktype&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DZakh/rescript-struct"&gt;rescript-struct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ostrowr/ts-json-validator"&gt;ts-json-validator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/badrap/valita"&gt;valita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jsoldi/to-typed"&gt;to-typed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/icebob/fastest-validator"&gt;fastest-validator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sant123/runtypes"&gt;runtypes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/neuledge/computed-types"&gt;computed_types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/paritytech/subshape"&gt;subshape&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/effect-ts/schema"&gt;effect-ts/schema&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marcj/TypeRunner"&gt;TypeRunner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/moodysalem/jointz"&gt;jointz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rhys-vdw/ts-auto-guard"&gt;ts-auto-guard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lukeautry/tserial"&gt;tserial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fabiandev/ts-runtime"&gt;ts-runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/stereobooster/type-o-rama"&gt;type-o-rama&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>typesystem</category>
    </item>
    <item>
      <title>Text to diagram</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sat, 23 Sep 2023 14:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/text-to-diagram-2pb3</link>
      <guid>https://dev.to/stereobooster/text-to-diagram-2pb3</guid>
      <description>&lt;p&gt;aka diagrams as code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stereobooster.com/content/posts/what-i-miss-in-markdown/index.md#diagrams"&gt;I dreamt about it for a long time&lt;/a&gt;. Idea is to allow to express diagrams with a text, this way you can draw diagram while you are writing you markdown file. Without need to switch to another tool. And this diagram becomes part of the document and can be stored in git.&lt;/p&gt;

&lt;p&gt;Now when &lt;a href="https://github.com/gohugoio/hugo/releases/tag/v0.93.0"&gt;Hugo supports&lt;/a&gt; render hooks for &lt;code&gt;code&lt;/code&gt; blocks and in &lt;a href="https://mdxjs.com/"&gt;MDX&lt;/a&gt; you can do this too (to use with Astro, etc.) it’s time to revisit the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Categories
&lt;/h2&gt;

&lt;p&gt;Before we move on let’s introduce some kind of categorisation of tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative&lt;/strong&gt; - when you have specific language for the job. You express what you want to get and it’s up to the tool to figure out how to draw it

&lt;ul&gt;
&lt;li&gt;Examples: &lt;a href="https://mermaid.js.org/"&gt;Mermaid&lt;/a&gt;, &lt;a href="https://graphviz.org/"&gt;Graphviz&lt;/a&gt;, &lt;a href="https://d2lang.com/"&gt;d2lang&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imperative&lt;/strong&gt; - more involved approach, you need to specify how to draw the diagram

&lt;ul&gt;
&lt;li&gt;Examples: ?&lt;/li&gt;
&lt;li&gt;Note: python + Matplotlib, for example. also in this category, but if we go that far we will reinvent &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt;. So let’s leave it out&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASCII-art&lt;/strong&gt; - you de-facto drawing, but with text. Tool just responsible for making it look nicer than terminal output

&lt;ul&gt;
&lt;li&gt;Examples: &lt;a href="https://ivanceras.github.io/svgbob-editor/"&gt;svgbob&lt;/a&gt;, &lt;a href="https://google.github.io/typograms/"&gt;typograms&lt;/a&gt;, &lt;a href="https://casual-effects.com/markdeep/"&gt;markdeep&lt;/a&gt;, &lt;a href="https://github.com/bep/goat"&gt;goat&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;output format&lt;/strong&gt; : svg, png (or any other raster image format), ASCII-art, html + css + js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;integration&lt;/strong&gt; : library, cli, web browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;supported diagrams&lt;/strong&gt; : network/graph, flowchart, UML and many more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;theming&lt;/strong&gt; : if you have to hardcode colours in the diagram or you can configure it later, for example to support dark theme&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;other&lt;/strong&gt; : code highlighting plugin for editor, auto-formatter/pretty-printer, support for icons/images, support for embedding html/markdown/LaTeX, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Names&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Integration&lt;/th&gt;
&lt;th&gt;Formats&lt;/th&gt;
&lt;th&gt;Supported diagrams&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://mermaid.js.org/"&gt;mermaid&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;browser*&lt;/td&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;td&gt;Flowchart, UML, ER, Gant…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://graphviz.org/"&gt;graphviz&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;CLI, library&lt;/td&gt;
&lt;td&gt;SVG, raster&lt;/td&gt;
&lt;td&gt;Graphs (flowchart, network, etc)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://d2lang.com/"&gt;d2lang&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;CLI (go)&lt;/td&gt;
&lt;td&gt;SVG, raster…&lt;/td&gt;
&lt;td&gt;Flowchart, UML, ER…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://pikchr.org/"&gt;pikchr&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;CLI, library&lt;/td&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;td&gt;&lt;a href="https://pikchr.org/home/uv/pic.pdf"&gt;Kind of anything&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://arthursonzogni.com/Diagon/"&gt;Diagon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;CLI, library&lt;/td&gt;
&lt;td&gt;ASCII-art&lt;/td&gt;
&lt;td&gt;Flowchart, Sequence…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://plantuml.com/"&gt;PlantUML&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;SVG, raster, ASCII-art&lt;/td&gt;
&lt;td&gt;UML…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://www.gnuplot.info/"&gt;gnuplot&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;SVG, raster, ASCII-art&lt;/td&gt;
&lt;td&gt;&lt;a href="https://gnuplot.sourceforge.net/demo_5.5/"&gt;plots&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://tikz.net/"&gt;Tikz&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;LaTeX*&lt;/td&gt;
&lt;td&gt;PDF*&lt;/td&gt;
&lt;td&gt;anything&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is birdeye overview. There are nuances. I didn’t include some libraries that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;has very narrow use-case (only one type of digram)&lt;/li&gt;
&lt;li&gt;are less popular or not supported (abandonware)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mermaid
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most popular, because it &lt;a href="https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/"&gt;was adopted by github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Works &lt;a href="https://github.com/mermaid-js/mermaid/issues/3650"&gt;only in the browser&lt;/a&gt;. There is a &lt;a href="https://github.com/mermaid-js/mermaid-cli"&gt;cli&lt;/a&gt;, but it just a hack with headless browser (puppeteer)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme-able&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bpruitt-goddard/vscode-mermaid-syntax-highlight"&gt;syntax highlightning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Graphviz
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Mature and wide-spread. I think, it was number one for graphs (flowchart, network, DAG) before github integrated mermaid&lt;/li&gt;
&lt;li&gt;Originally CLI tool, but there are as well libraries, for example &lt;a href="https://www.npmjs.com/package/@hpcc-js/wasm"&gt;WASM-based&lt;/a&gt;, which allows to use Graphwiz on the server and in the browser&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=joaompinto.vscode-graphviz"&gt;syntax highlightning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  d2lang
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://text-to-diagram.com/"&gt;Wants to take over the world&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Originally CLI tool, but there is &lt;a href="https://github.com/terrastruct/d2/issues/136"&gt;an attempt to convert to WASM library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It worth to mention that it packs headless browser (playwright)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme-able&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=terrastruct.d2"&gt;syntax highlightning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pikchr
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“&lt;a href="https://en.wikipedia.org/wiki/Pic_language"&gt;PIC&lt;/a&gt;-like markup language for diagrams in technical documentation”&lt;/li&gt;
&lt;li&gt;To me it feels more on declarative side, but you can do some imperative things as well&lt;/li&gt;
&lt;li&gt;There is &lt;a href="https://github.com/fabiospampinato/pikchr-wasm"&gt;WASM library&lt;/a&gt; and &lt;a href="https://github.com/gopikchr/gopikchr"&gt;go port&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epmoyer/pikchr_monarch"&gt;syntax highlightning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Diagon
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;“Diagon is an interactive interpreter. It transforms markdown-style expression into an ascii-art representation.”&lt;/li&gt;
&lt;li&gt;It is the only one option on the list which outputs (only) ASCII-art. But you can feed it to another tool which will convert it to SVG&lt;/li&gt;
&lt;li&gt;There is WASM library, but it’s not distributed as npm package&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tikz
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The most famous option between people who use LaTeX&lt;/li&gt;
&lt;li&gt;I can’t tell if this more declarative or imperative approach (kind of both)&lt;/li&gt;
&lt;li&gt;There is a project which &lt;a href="https://github.com/kisonecat/web2js"&gt;compiles Tikz to WASM&lt;/a&gt; (&lt;a href="https://tikzjax.com/"&gt;demo&lt;/a&gt;), but it’s not distributed as npm package&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  gnuplot
&lt;/h3&gt;

&lt;p&gt;This is the only one solution on the list which focuses solely on plots. There are a lot of plot libraries and I don’t want to list them all here. The difference from other solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is standalone, while other solutions are DSLs/libraries inside bigger language, for example &lt;code&gt;matplotlib&lt;/code&gt; “requires” python&lt;/li&gt;
&lt;li&gt;it is CLI, where is many other solutions are GUI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One way or another I felt like I need to include at least one of those on the list. Originally written in C, so there is a &lt;a href="https://github.com/chhu/gnuplot-JS/issues/5"&gt;chance of porting it to WASM&lt;/a&gt; (see also &lt;a href="https://mneuroth.github.io/MobileGnuplotViewerQuickWASM/"&gt;MobileGnuplotViewerQuickWASM&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I can’t tell if this more declarative or imperative approach (kind of both)&lt;/p&gt;

&lt;p&gt;Syntax highlightning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=fizzybreezy.gnuplot"&gt;fizzybreezy.gnuplot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mammothb.gnuplot"&gt;mammothb.gnuplot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=MarioSchwalbe.gnuplot"&gt;MarioSchwalbe.gnuplot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ASCII-art
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ivanceras.github.io/svgbob-editor/"&gt;svgbob&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://google.github.io/typograms/"&gt;typograms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://casual-effects.com/markdeep/"&gt;markdeep&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bep/goat"&gt;goat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ditaa.sourceforge.net/"&gt;diita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/asciitosvg/asciitosvg"&gt;asciitosvg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blockdiag.com/en/"&gt;blockdiag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/christiangoltz/shaape"&gt;shaape&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Editors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://asciiflow.com/"&gt;https://asciiflow.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://textik.com/"&gt;https://textik.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kirilllive.github.io/ASCII_Art_Paint/ascii_paint.html"&gt;https://kirilllive.github.io/ASCII_Art_Paint/ascii_paint.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Special purpose
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wavedrom/wavedrom"&gt;wavedrom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jacquev6.github.io/DrawGrammar/"&gt;DrawGrammar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sverweij/state-machine-cat"&gt;state-machine-cat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://structurizr.com/"&gt;structurizr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kevinpt/symbolator"&gt;symbolator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kevinpt.github.io/syntrax/"&gt;syntrax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://upset.js.org/venn.js/"&gt;venn.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Deep-Symmetry/bytefield-svg"&gt;bytefield-svg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lilypond.org/"&gt;lilypond&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Other
&lt;/h3&gt;

&lt;p&gt;Other tools (I’m pretty sure there are much more projects which I’m not aware of):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://penrose.cs.cmu.edu/docs/ref/style/usage"&gt;penrose&lt;/a&gt;. This one is &lt;strong&gt;interesting&lt;/strong&gt; - I need to read more about it&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vega.github.io/vega-lite/"&gt;vega-lite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.mcternan.me.uk/mscgen/"&gt;mscgen&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mscgen.js.org/"&gt;mscgen_js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flowchart.js.org/"&gt;flowchart.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bramp.github.io/js-sequence-diagrams/"&gt;js-sequence-diagrams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kroki.io/#support"&gt;kroki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/softwaretechnik-berlin/dbml-renderer"&gt;dbml-renderer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/aplevich/dpic"&gt;dpic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BurntSushi/erd"&gt;erd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blockdiag.com/en/actdiag/index.html"&gt;actdiag&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nomnoml.com/"&gt;nomnoml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.umlet.com/"&gt;UMLet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://xosh.org/text-to-diagram/"&gt;https://xosh.org/text-to-diagram/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.asciidoctor.org/diagram-extension/latest/"&gt;https://docs.asciidoctor.org/diagram-extension/latest/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Instead of conclusion
&lt;/h2&gt;

&lt;p&gt;I’m looking for solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to embed diagrams in markdown file (inside fenced code block)&lt;/li&gt;
&lt;li&gt;ideally it should be possible to generate on the server (during conversion of markdown to HTML)&lt;/li&gt;
&lt;li&gt;ideally it should generate SVG, If we inline SVG (instead of using &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;) it can support

&lt;ul&gt;
&lt;li&gt;dark mode via classes or css variables&lt;/li&gt;
&lt;li&gt;it can be animated&lt;/li&gt;
&lt;li&gt;it can contain html links (&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;you can use Cmd + F to search text in it&lt;/li&gt;
&lt;li&gt;we can add zoom/pan to it as in &lt;a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams#creating-mermaid-diagrams"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;sometime you need to have standalone file as well, which you can include as image for crossposting&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;it should be lightweight (so no headless browsers)&lt;/li&gt;
&lt;li&gt;In some cases it make sense to generate visualisation on the client side (in the browser), for example:

&lt;ul&gt;
&lt;li&gt;fractals with infinite zoom&lt;/li&gt;
&lt;li&gt;interactive visualisations, like in &lt;a href="https://github.com/bijection/g9"&gt;g9&lt;/a&gt; or &lt;a href="https://reality.mentat.org/essays/reality/introduction"&gt;Road to Reality Essays&lt;/a&gt;. In this case they should use high performance solutions (canvas or WebGL)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Diagrams that I’m interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;graphs (aka network, flowchart, DAG, etc.)

&lt;ul&gt;
&lt;li&gt;Hasse diagrams&lt;/li&gt;
&lt;li&gt;subway-scheme like diagrams. For example see: &lt;a href="https://mermaid.js.org/syntax/gitgraph.html"&gt;gitgraph&lt;/a&gt;, &lt;a href="https://metrosets.ac.tuwien.ac.at/"&gt;metrosets&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Euler diagrams (which are often confused with Venn)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/"&gt;xkcd-style&lt;/a&gt; plots&lt;/li&gt;
&lt;li&gt;sankey diagrams&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more twist to this story. Because those diagrams can be represented as text you can use &lt;strong&gt;GhatGPT&lt;/strong&gt; to generate diagrams.&lt;/p&gt;

</description>
      <category>diagram</category>
      <category>markdown</category>
      <category>mermaid</category>
      <category>graphviz</category>
    </item>
    <item>
      <title>Portable markdown links</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Fri, 22 Sep 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/portable-markdown-links-2bn0</link>
      <guid>https://dev.to/stereobooster/portable-markdown-links-2bn0</guid>
      <description>&lt;p&gt;&lt;strong&gt;aka&lt;/strong&gt; relative links, file path links. Software supports &lt;strong&gt;portable markdown links&lt;/strong&gt; if for content like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[test](folder/test.md)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;editor&lt;/strong&gt; : user can Cmd + Click the link and editor will jump to that file&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;static website generator&lt;/strong&gt; : file pahts are replaced by URLs according generation scheme, for example: &lt;code&gt;[test](folder/test.md)&lt;/code&gt; → &lt;code&gt;&amp;lt;a href="/folder/test/"&amp;gt;test&amp;lt;/a&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/bep/portable-hugo-links"&gt;portable-hugo-links&lt;/a&gt; shows how same links work in Github and website generated by Hugo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Links: &lt;code&gt;[readme](/readme.md)&lt;/code&gt;, which also includes resolution of relative and absolute paths:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[readme](../readme.md)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[readme](./readme.md)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[readme](readme.md)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Anchors: &lt;code&gt;&lt;a href="///readme.md#heading"&gt;readme&lt;/a&gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;for editor: Cmd + Click should navigate to specified section&lt;/li&gt;
&lt;li&gt;for static website generator: anchors should be preserved in html links&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Images: &lt;code&gt;![alt]_(img.jpg)&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;for editor’s preview: images should be displayed&lt;/li&gt;
&lt;li&gt;for static website generator: images should be displayed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who supports it?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;Does it support PML&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Github&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VSCode&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo&lt;/td&gt;
&lt;td&gt;Yes, &lt;a href="https://github.com/bep/portable-hugo-links"&gt;with configuration&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docusaurus&lt;/td&gt;
&lt;td&gt;Yes, see &lt;a href="https://docusaurus.io/docs/markdown-features/links"&gt;documentation&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/microsoft/vscode/tree/main/extensions/markdown-language-features/server"&gt;Markdown Language Server&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/tchayen/markdown-links"&gt;markdown-links&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll&lt;/td&gt;
&lt;td&gt;Yes, with &lt;a href="https://github.com/benbalter/jekyll-relative-links"&gt;plugin&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Astro&lt;/td&gt;
&lt;td&gt;No, see &lt;a href="https://github.com/withastro/roadmap/discussions/424"&gt;discussion&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;No, AFAIK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gatsby&lt;/td&gt;
&lt;td&gt;No, AFAIK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Naming
&lt;/h2&gt;

&lt;p&gt;Why do I call it &lt;strong&gt;portable&lt;/strong&gt;? Because this simple convention allows to use same markdown files with different software wihtout modifications. In the same vein, we can think of portable markdown frontmatter, etc.&lt;/p&gt;

&lt;p&gt;A bit of convention over configuration goes long way.&lt;/p&gt;

</description>
      <category>markdown</category>
    </item>
    <item>
      <title>Speed up navigation between pages</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Tue, 19 Sep 2023 08:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/speed-up-navigation-between-pages-5bki</link>
      <guid>https://dev.to/stereobooster/speed-up-navigation-between-pages-5bki</guid>
      <description>&lt;p&gt;Basic idea - navigating between pages is slow, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser waits for network response&lt;/li&gt;
&lt;li&gt;it is expensive to redraw the whole page from scratch. Much faster would be to replace &lt;code&gt;innerHTML&lt;/code&gt; of the document. At least this is common belief, but there are &lt;a href="https://jakearchibald.com/2016/fun-hacks-faster-content/"&gt;nuances to that&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  History
&lt;/h2&gt;

&lt;p&gt;First solution appeared in 2010. See &lt;a href="https://github.com/defunkt/jquery-pjax"&gt;defunkt/jquery-pjax&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Around 2015 "olympic torch" was taken over by &lt;a href="https://github.com/turbolinks/turbolinks"&gt;turbolinks&lt;/a&gt;. Which was part of Rails stack and later replaced by &lt;a href="https://turbo.hotwired.dev/handbook/drive"&gt;Turbo Drive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Around the same time there were also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MoOx/pjax"&gt;MoOx/pjax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/falsandtru/pjax-api#features"&gt;falsandtru/pjax-api&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://barba.js.org/"&gt;barba&lt;/a&gt;, 2016&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/swup/swup"&gt;swup&lt;/a&gt;, 2017&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2018 Google jump in on that boat with &lt;a href="https://github.com/GoogleChromeLabs/quicklink"&gt;quicklink&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: later doesn't mean better, because development didn't stop at the release date. For example, &lt;code&gt;swup&lt;/code&gt; recently released v4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it still alive?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Yes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One would think that with SPA trend (started around 2015) nobody would care about this kind of solutions, but static website still popular. For example Hugo, Astro and &lt;a href="https://jamstack.org/generators/"&gt;others&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The devil is in the details
&lt;/h2&gt;

&lt;p&gt;I put all those libraries in one category but they are actually different and use different assumptions. For example,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PJAX uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/History_API"&gt;History API&lt;/a&gt; and AJAX (or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch"&gt;fetch&lt;/a&gt;). It takes over browser navigation mechanism&lt;/li&gt;
&lt;li&gt;quicklink on the other side bothers itself only with &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Prefetch"&gt;prefetching&lt;/a&gt;/&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/prerender"&gt;prerendering&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Browser history
&lt;/h3&gt;

&lt;p&gt;So we have &lt;strong&gt;two main approaches&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;don't take over browser navigation

&lt;ul&gt;
&lt;li&gt;pros:&lt;/li&gt;
&lt;li&gt;easy to integrate&lt;/li&gt;
&lt;li&gt;cons:&lt;/li&gt;
&lt;li&gt;animations are not possible&lt;/li&gt;
&lt;li&gt;state of the page reseted, like selected tabs, collapsed menu, scroll position in sidebar&lt;/li&gt;
&lt;li&gt;examples: &lt;code&gt;quicklink&lt;/code&gt;, &lt;a href="https://docs.astro.build/en/guides/integrations-guide/prefetch/"&gt;@astrojs/prefetch&lt;/a&gt;, &lt;a href="https://code.juliancataldo.com/component/astro-hover-prefetch/"&gt;astro-hover-prefetch&lt;/a&gt;, &lt;a href="https://swup.js.org/plugins/preload-plugin/"&gt;swup preload&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;take over browser navigation

&lt;ul&gt;
&lt;li&gt;pros:&lt;/li&gt;
&lt;li&gt;hard to integrate&lt;/li&gt;
&lt;li&gt;cons:&lt;/li&gt;
&lt;li&gt;animations are possible&lt;/li&gt;
&lt;li&gt;state of the page can be preserved but needs additional work&lt;/li&gt;
&lt;li&gt;examples: &lt;code&gt;pjax&lt;/code&gt;, &lt;code&gt;barba&lt;/code&gt;, &lt;code&gt;swup&lt;/code&gt;, &lt;a href="https://github.com/RafidMuhymin/astro-spa"&gt;astro-spa&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adaptability
&lt;/h3&gt;

&lt;p&gt;Besides browser navigation we need to take into account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we don't want to prefetch all the links

&lt;ul&gt;
&lt;li&gt;quicklink uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"&gt;Intersection Observer&lt;/a&gt; to detect links within the viewport&lt;/li&gt;
&lt;li&gt;Other approach is to do this on hover, focus, tap&lt;/li&gt;
&lt;li&gt;Some libraries allows to specify which links to prefetch&lt;/li&gt;
&lt;li&gt;Or we can statistically &lt;a href="https://github.com/guess-js/guess"&gt;guess&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;we don't want it to do while browser is busy doing something else. You don't want to degrade user experience

&lt;ul&gt;
&lt;li&gt;quicklink uses &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback"&gt;requestIdleCallback&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Making additional requests on mobile devices may cost user additional money

&lt;ul&gt;
&lt;li&gt;quicklink uses &lt;code&gt;navigator.connection.effectiveType&lt;/code&gt; to detect slow networks, and &lt;code&gt;navigator.connection.saveData&lt;/code&gt; to detect data-saver mode&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related functionality
&lt;/h3&gt;

&lt;p&gt;Main subject is to speed up navigation between pages, but there are related functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;animate&lt;/strong&gt; page navigations, like in &lt;code&gt;swup&lt;/code&gt; and &lt;code&gt;barba&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;replace only part of the page&lt;/strong&gt;, for example to filter list or table, like in &lt;a href="https://turbo.hotwired.dev/handbook/frames"&gt;Turbo Frames&lt;/a&gt;, &lt;a href="https://swup.js.org/plugins/fragment-plugin/"&gt;swup fragment&lt;/a&gt;, &lt;a href="https://docs.astro.build/en/guides/view-transitions/"&gt;astro view transitions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;show &lt;strong&gt;page preview&lt;/strong&gt; on hover, like in &lt;a href="https://help.obsidian.md/Plugins/Page+preview"&gt;Obsidian&lt;/a&gt;. See also &lt;a href="https://github.com/Fischer-L/previewbox"&gt;previewbox&lt;/a&gt; and &lt;a href="https://github.com/cijiugechu/astro-link-preview"&gt;astro-link-preview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;highlight section if navigated with anchor, like in StakOverflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I mainly concern myself with static web sites, but in the context of dynamic websites there is so called "HTML over wire" approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elixir &lt;a href="https://github.com/phoenixframework/phoenix_live_view"&gt;Phoenix LiveView&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ruby &lt;a href="https://turbo.hotwired.dev/"&gt;Turbo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PHP &lt;a href="https://laravel-livewire.com/"&gt;Laravel livewire&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Complexity with browser history
&lt;/h3&gt;

&lt;p&gt;Basic implementation can look like this&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add global click event listener&lt;/li&gt;
&lt;li&gt;on click you fetch remote content&lt;/li&gt;
&lt;li&gt;extract element that needs to be replaced (for example &lt;code&gt;main&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;replace content with &lt;code&gt;target.innerHTML = response&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Push new item to history with &lt;code&gt;history.pushState(state, "", url);&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt; you need to take care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removing event handlers from old HTML&lt;/li&gt;
&lt;li&gt;attaching event handlers to new HTML&lt;/li&gt;
&lt;li&gt;replacing title, maybe announce it to screen reader&lt;/li&gt;
&lt;li&gt;replacing styles&lt;/li&gt;
&lt;li&gt;replacing scripts&lt;/li&gt;
&lt;li&gt;maybe provide a hook so that it would be possible to integrate analytics&lt;/li&gt;
&lt;li&gt;scrolling to top of the page&lt;/li&gt;
&lt;li&gt;listen to history for back navigations&lt;/li&gt;
&lt;li&gt;maybe resolve relative links&lt;/li&gt;
&lt;li&gt;it doesn't preserve state of scroll or tabs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is an error-prone approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  To be continued...
&lt;/h2&gt;

&lt;p&gt;While I was doing the research on the subject I stumbled upon:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/docs/web-platform/view-transitions/"&gt;View transitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API"&gt;Navigation API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wicg.github.io/nav-speculation/prerendering.html"&gt;"Prerender 2"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/bfcache/"&gt;Back/forward cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/cache-api-quick-guide/"&gt;Cache API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will need more time to finish the research&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>Markdown parsers</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sun, 17 Sep 2023 10:00:09 +0000</pubDate>
      <link>https://dev.to/stereobooster/markdown-parsers-171g</link>
      <guid>https://dev.to/stereobooster/markdown-parsers-171g</guid>
      <description>&lt;h2&gt;
  
  
  JS
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/micromark/micromark"&gt;micromark&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/syntax-tree/mdast-util-from-markdown"&gt;mdast-util-from-markdown&lt;/a&gt; used by&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/remarkjs/remark/tree/main/packages/remark-parse"&gt;remark-parse&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://prettier.io/blog/2017/11/07/1.8.0.html"&gt;prettier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mdx-js/mdx/tree/main/packages/mdx"&gt;mdx&lt;/a&gt;, which is used by&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gatsbyjs.com/"&gt;Gatsby&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docusaurus.io/"&gt;Docusaurus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/remark-mdc"&gt;mdc&lt;/a&gt;, which is used by&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nuxt.com/"&gt;Nuxt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://astro.build/"&gt;Astro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/markdown-it/markdown-it"&gt;markdown-it&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.11ty.dev/docs/languages/markdown/"&gt;11ty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v2.vuepress.vuejs.org/"&gt;vuepress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vitepress.dev/"&gt;vitepress&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/showdownjs/showdown"&gt;showdown&lt;/a&gt; used by (based on what they say)

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/GoogleCloudPlatform"&gt;GoogleCloudPlatform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meteor.com/"&gt;Meteor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Stackexchange - forked as &lt;a href="https://code.google.com/archive/p/pagedown/"&gt;PageDown&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jonschlinkert/remarkable"&gt;remarkable&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;used by &lt;a href="https://docusaurus.io/"&gt;Docusaurus&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/markedjs/marked"&gt;marked&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hexo.io/index.html"&gt;Hexo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/docsifyjs/docsify"&gt;docsify&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/markup-it"&gt;markup-it&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.gitbook.com/"&gt;gitbook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/commonmark/commonmark.js/"&gt;commonmark.js&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;is the JS &lt;strong&gt;reference implementation of CommonMark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WASM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/rsms/markdown-wasm"&gt;markdown-wasm&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;uses &lt;strong&gt;md4c&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://markdown.fastlylabs.com/"&gt;Wasm Markdown&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;uses &lt;strong&gt;pulldown-cmark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/rodneylab/parsedown"&gt;parsedown&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;uses &lt;strong&gt;pulldown-cmark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  See also
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://umemotoctrl.github.io/mdpjs/"&gt;Online playground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://npmtrends.com/commonmark-vs-markdown-it-vs-marked-vs-markup-it-vs-micromark-vs-remark-parse-vs-remarkable-vs-showdown"&gt;npmtrends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://biomejs.dev/internals/language-support/"&gt;biome&lt;/a&gt; promises to add markdown eventually&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rust
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/raphlinus/pulldown-cmark"&gt;pulldown-cmark&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rust-lang/mdBook"&gt;mdBook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cobalt-org.github.io/"&gt;cobalt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/getzola/zola/tree/master/components/markdown"&gt;zola-markdown&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;uses &lt;a href="https://pest.rs/"&gt;pest.rs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;used by &lt;a href="https://github.com/getzola/zola"&gt;zola&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wooorm/markdown-rs"&gt;markdown-rs&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Sibling to &lt;strong&gt;micromark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kivikakk/comrak"&gt;comrak&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Rust port of &lt;strong&gt;cmark-gfm&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/markdown-it-rust/markdown-it"&gt;markdown-it-rust&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Rust port of &lt;strong&gt;markdown-it&lt;/strong&gt; library.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Trivernis/snekdown"&gt;Snekdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Golang
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/yuin/goldmark"&gt;goldmark&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/getting-started/configuration-markup/"&gt;Hugo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gomarkdown/markdown"&gt;gomarkdown/markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/golang-commonmark/markdown"&gt;golang-commonmark/markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/russross/blackfriday"&gt;blackfriday&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mmarkdown/mmark"&gt;mmark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/src.elv.sh/pkg/md"&gt;md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  C
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/github/cmark-gfm"&gt;cmark-gfm&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;used by Github&lt;/li&gt;
&lt;li&gt;is an extended version of the &lt;strong&gt;cmark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/commonmark/cmark"&gt;cmark&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;is the C &lt;strong&gt;reference implementation of CommonMark&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mity/md4c"&gt;md4c&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Zig
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kivikakk/koino"&gt;koino&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Zig port of &lt;strong&gt;comrak&lt;/strong&gt;, which is Rust port of &lt;strong&gt;cmark-gfm&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/demizer/markzig"&gt;markzig&lt;/a&gt; archived&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tree-sitter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tree-sitter.github.io/tree-sitter/"&gt;Tree-sitter&lt;/a&gt; is a parser generator tool and an incremental parsing library. There are bindings for many languages (including Rust and JS)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MDeiml/tree-sitter-markdown"&gt;MDeiml/tree-sitter-markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ikatyang/tree-sitter-markdown"&gt;ikatyang/tree-sitter-markdown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ruby
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/jekyll/jekyll-commonmark"&gt;jekyll-commonmark&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;used by &lt;a href="https://jekyllrb.com/docs/configuration/markdown/"&gt;jekyll&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;uses &lt;strong&gt;cmark-gfm&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vmg/redcarpet"&gt;redcarpet&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/slatedocs/slate"&gt;slate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jekyllrb.com/docs/configuration/markdown/"&gt;jekyll&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Python
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://python-markdown.github.io/"&gt;python-markdown&lt;/a&gt; used by

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mkdocs.org/"&gt;MkDocs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://getpelican.com/"&gt;Pelican&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/frostming/marko"&gt;marko&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Clojure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/askonomm/clarktown"&gt;clarktown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Elixir
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/am-kantox/md"&gt;md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/BubuAnabelas/awesome-markdown#libraries"&gt;"awesome-markdown" libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lezer-parser/markdown"&gt;lezer-parser/markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://babelmark.github.io/"&gt;babelmark&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Crticism
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jgm/djot#rationale"&gt;djot rationale&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.swyx.io/markdown-mistakes"&gt;markdown mistakes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>markdown</category>
      <category>parser</category>
      <category>javascript</category>
      <category>go</category>
    </item>
    <item>
      <title>Component libraries trends</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sat, 16 Sep 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/component-libraries-trends-12fn</link>
      <guid>https://dev.to/stereobooster/component-libraries-trends-12fn</guid>
      <description>&lt;h2&gt;
  
  
  Headless and accessible
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;aka&lt;/strong&gt; unstyled, renderless&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main idea&lt;/strong&gt; is to provide all the logic for component (often accessibility) without forcing any visual appearance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/reach/reach-ui/issues/972"&gt;Reach UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.radix-ui.com/primitives/docs/overview/introduction"&gt;Radix primitives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://headlessui.com/"&gt;headlessui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ariakit.org/components"&gt;ariakit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;not a component library, but worth to mention &lt;a href="https://react-spectrum.adobe.com/"&gt;React Spectrum&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Based on a design system
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Main idea&lt;/strong&gt; it is based on thoroughly documented design-system, so it gives more than just components. There can be a lot of implementations based on the same design system. For example, for &lt;a href="https://m3.material.io/"&gt;material design&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mui.com/"&gt;mui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v2.vuetifyjs.com/en/"&gt;vuetify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://suid.io/"&gt;SUID&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other design systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://designsystemsrepo.com/design-systems/"&gt;https://designsystemsrepo.com/design-systems/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  For mobile development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Main idea&lt;/strong&gt; is to support mobile devices, for example, &lt;a href="https://daisyui.com/components/bottom-navigation/"&gt;bottom navigation bar&lt;/a&gt;, &lt;a href="https://github.com/bmcmahen/react-gesture-stack"&gt;gestures&lt;/a&gt;, &lt;a href="https://konstaui.com/react/safe-areas"&gt;safe-areas&lt;/a&gt;, &lt;a href="https://tamagui.dev/docs/components/sheet/1.59.0"&gt;action-sheet&lt;/a&gt; etc. Can be used for developing native apps with WebView (PhoneGap/Cordova) or for PWAs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://konstaui.com/"&gt;Konsta UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://daisyui.com/components/"&gt;daisyUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sancho-ui.com/"&gt;Sancho&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://framework7.io/kitchen-sink/core/"&gt;Framework7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vant-ui.github.io/vant/mobile.html#/en-US"&gt;Vant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://onsen.io/"&gt;Onsen UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mobileui.github.io/#getting-started"&gt;mobileui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All react-native-web component libraries are also in this category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tamagui.dev/"&gt;tamagui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://reactnativepaper.com/"&gt;paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wix.github.io/react-native-ui-lib/"&gt;RNUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://magnus-ui.com/"&gt;magnus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://akveo.github.io/react-native-ui-kitten/docs/guides/running-on-the-web#existing-expo-applications"&gt;React Native UI Kitten&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ui.gluestack.io/"&gt;gluestack-ui&lt;/a&gt;, successor for &lt;a href="https://nativebase.io/"&gt;NativeBase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Based on Tailwind
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Main idea&lt;/strong&gt;: it is based on Tailwind. While CSS-in-JS in very unstable situation and they keep inventing zero-runtime solutions, Tailwind just works. So there is whole new wave of component libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://daisyui.com/components/"&gt;daisyUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konstaui.com/"&gt;Konsta UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nextui.org/"&gt;NextUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.flowbite-react.com/"&gt;Flowbite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/creativetimofficial/material-tailwind"&gt;Material Tailwind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://preline.co/examples.html"&gt;preline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ripple-ui.com/"&gt;ripple-ui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwind-elements.com/docs/react/"&gt;tailwind-elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sailboatui.com/docs/components/tooltip/"&gt;sailboatui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not a component library
&lt;/h2&gt;

&lt;p&gt;This is new interesting approach. We already have enough good component libraries, so instead of creating another one, this tool allows to re-use existing. It is more like collection of snippets.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://ui.shadcn.com/docs"&gt;shadcn/ui&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>css</category>
      <category>components</category>
    </item>
    <item>
      <title>Distributing CSS in npm package</title>
      <dc:creator>stereobooster</dc:creator>
      <pubDate>Sat, 09 Sep 2023 15:00:00 +0000</pubDate>
      <link>https://dev.to/stereobooster/distributing-css-in-npm-package-16lf</link>
      <guid>https://dev.to/stereobooster/distributing-css-in-npm-package-16lf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Typical problem: you want to distribute React (Solid, Vue, etc.) component in npm package. Most likely it will need some kind of styles and maybe assets (images, svg, fonts). How you're gonna do it. Two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;distribute CSS files&lt;/li&gt;
&lt;li&gt;CSS-in-JS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Distributing CSS has following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need explicitly include those files (dependency)&lt;/li&gt;
&lt;li&gt;You may need to process them (with bundler/compiler) in order to adjust paths&lt;/li&gt;
&lt;li&gt;They may result in dead code (e.g. code that is never used)&lt;/li&gt;
&lt;li&gt;Styles may clash (global namespace, isolation)&lt;/li&gt;
&lt;li&gt;There can be issues with non-deterministic resolution&lt;/li&gt;
&lt;li&gt;No customisation (no theme support)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Distributing CSS-in-JS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resolves all CSS issues, but also&lt;/li&gt;
&lt;li&gt;adds runtime penalty&lt;/li&gt;
&lt;li&gt;increases bundle size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is classical point of view, but there is a twist. Picture may change a bit with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;atomic CSS&lt;/li&gt;
&lt;li&gt;CSS variables (custom properties)&lt;/li&gt;
&lt;li&gt;zero-runtime CSS-in-JS (compilation)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CSS-in-JS alternatives
&lt;/h2&gt;

&lt;p&gt;Before we continue let's mention alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;atomic CSS UI kit - &lt;a href="https://2023.stateofcss.com/en-US/css-frameworks/"&gt;Tailwind it the most popular option here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/css-modules/css-modules"&gt;CSSModules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to distribute components (in npm package) you would have to compile those to CSS and distribute this file, which brings us back to CSS issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions/8814"&gt;there is a way to compile Tailwind inside the npm package&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://www.npmjs.com/package/@dlarroder/playground?activeTab=code"&gt;@dlarroder/playground&lt;/a&gt; (&lt;a href="https://www.trongtomo.com/blog/create-and-publish-your-own-react-component-library-with-typescript-storybook-and-tailwind"&gt;blog post&lt;/a&gt;)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It contains global styles:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;*,&lt;/span&gt;
&lt;span class="nd"&gt;:after&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#e5e7eb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;You may be able to customize it via CSS variables
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;*,&lt;/span&gt;
&lt;span class="nd"&gt;:after&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nd"&gt;:before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--tw-ring-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;246&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;But there is no type-safety. I can do a typo &lt;code&gt;--tw-ring-col: rgba(59, 130, 246, 0.5);&lt;/code&gt; and nothing will notify me about the error&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;What if you use two component libraries that use different versions of Tailwind. There will be clash&lt;/li&gt;
&lt;li&gt;CSS file may contain all styles for all components, but if I use only one component all the rest of styles will be a dead code&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  CSS-in-JS issues
&lt;/h2&gt;

&lt;p&gt;The biggest problem is runtime penalty. Your components would need to parse code for CSS (template literals or JS object), do a vendor specific prefixing, and inject styles and all this happens in the main thread.&lt;/p&gt;

&lt;p&gt;Second problem is increased bundle size, which includes runtime itself and CSS expressed in JS.&lt;/p&gt;

&lt;p&gt;Don't forget about server side rendering, which also would need special handling.&lt;/p&gt;

&lt;p&gt;As the solution you may use one of zero-runtime approaches (in alphabetic order):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://compiledcssinjs.com/"&gt;compiledcssinjs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linaria.dev/"&gt;linaria&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://macaron.js.org/"&gt;macaron&lt;/a&gt; uses &lt;code&gt;vanilla-extract&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://panda-css.com/"&gt;panda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/johanholmerin/style9/blob/master/docs/How-it-works.md"&gt;style9&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/wmertens/styled-vanilla-extract"&gt;styled-vanilla-extract&lt;/a&gt; uses &lt;code&gt;vanilla-extract&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tamagui/tamagui/blob/becbd88dd46fc43b4c0a838c33a19f07cb578542/packages/vite-plugin/src/extract.ts#L1"&gt;tamagui&lt;/a&gt; inspired by &lt;code&gt;vanilla-extract&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unocss.dev/guide/"&gt;unocss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vanilla-extract.style/"&gt;vanilla-extract&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But then again if you "compile" CSS-in-JS before distributing via npm you will end up with "style" file. So you need to distribute it as is and compilation should be done by the consumer, which may be problematic. Because there are a lot of bundlers/compilers, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;webpack/babel&lt;/li&gt;
&lt;li&gt;vite/esbuild&lt;/li&gt;
&lt;li&gt;turbopack/swc&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you either will vendor-lock your consumers to one solution or CSS-in-JS need to provide all options.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;solution&lt;/th&gt;
&lt;th&gt;vite&lt;/th&gt;
&lt;th&gt;esbuild&lt;/th&gt;
&lt;th&gt;webpack&lt;/th&gt;
&lt;th&gt;next&lt;/th&gt;
&lt;th&gt;parcel&lt;/th&gt;
&lt;th&gt;rollup&lt;/th&gt;
&lt;th&gt;babel&lt;/th&gt;
&lt;th&gt;cli&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://vanilla-extract.style/documentation/getting-started/#bundler-integration"&gt;vanilla-extract&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/callstack/linaria/blob/master/docs/BUNDLERS_INTEGRATION.md"&gt;linaria&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://panda-css.com/docs/installation/cli"&gt;panda&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://compiledcssinjs.com/docs/installation"&gt;compiledcssinjs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: this is not a fair comparison - devil is in details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world experience
&lt;/h2&gt;

&lt;p&gt;What do big component libraries (UI kits) choose to use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use compile-time CSS-in-JS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;mui: &lt;a href="https://github.com/mui/material-ui/issues/38137"&gt;[RFC][system] Zero-runtime CSS-in-JS implementation&lt;/a&gt;. But currently uses emotion&lt;/li&gt;
&lt;li&gt;Chakra UI: &lt;a href="https://www.adebayosegun.com/blog/the-future-of-chakra-ui"&gt;The future of Chakra UI&lt;/a&gt;. Currently uses &lt;a href="https://github.com/chakra-ui/panda"&gt;panda&lt;/a&gt;. Before was using emotion.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Tailwind
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/themesberg/flowbite-react"&gt;flowbite-react&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://daisyui.com/"&gt;daisyui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rewind-ui.dev/"&gt;rewind-ui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;and &lt;a href="https://github.com/aniftyco/awesome-tailwindcss#ui-libraries-components--templates"&gt;others&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use runtime CSS-in-JS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/uber/baseweb"&gt;baseweb&lt;/a&gt; uses styletron&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mantinedev/mantine"&gt;mantime&lt;/a&gt; uses emotion based css-in-js library&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.fluentui.dev/"&gt;fluentui&lt;/a&gt;: &lt;a href="https://github.com/microsoft/fluentui/wiki/Component-Styling#motivations-for-moving-away-from-scss"&gt;Motivations for moving away from SCSS&lt;/a&gt;. Currently uses @fluentui/merge-styles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://evergreen.segment.com/"&gt;evergreen&lt;/a&gt; uses &lt;a href="https://github.com/segmentio/ui-box"&gt;ui-box&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/grommet/grommet"&gt;grommet&lt;/a&gt; uses styled-components&lt;/li&gt;
&lt;li&gt;All &lt;a href="https://necolas.github.io/react-native-web/"&gt;react-native-web&lt;/a&gt; based systems:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactnativepaper.com/"&gt;paper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wix.github.io/react-native-ui-lib/"&gt;RNUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://magnus-ui.com/"&gt;magnus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://akveo.github.io/react-native-ui-kitten/docs/guides/running-on-the-web#existing-expo-applications"&gt;React Native UI Kitten&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ui.gluestack.io/"&gt;gluestack-ui&lt;/a&gt;, successor for &lt;a href="https://nativebase.io/"&gt;NativeBase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use "nothing"
&lt;/h3&gt;

&lt;p&gt;There is a trend for un-styled components (aka renderless, headless). They use "nothing", but this becomes responsibility of consumer to provide styles (including essential ones):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.radix-ui.com/primitives/docs/guides/styling"&gt;radix-ui primitives&lt;/a&gt; uses &lt;code&gt;style&lt;/code&gt; prop and CSS variables&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ariakit.org/guide/styling"&gt;ariakit&lt;/a&gt; uses &lt;code&gt;style&lt;/code&gt; prop and CSS variables&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/tailwindlabs/headlessui"&gt;headlessui&lt;/a&gt; can be used with Tailwind&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reach.tech/"&gt;reach-ui&lt;/a&gt; uses CSS for essential styles&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mui/material-ui/tree/master/packages/mui-base"&gt;@mui/base&lt;/a&gt; uses &lt;code&gt;useUtilityClasses&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use CSS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ant.design"&gt;Ant design&lt;/a&gt; (less)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.semantic-ui.com/theming/"&gt;Semanitc UI&lt;/a&gt; (less)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/carbon-design-system/carbon/tree/main"&gt;carbon&lt;/a&gt; (SCSS)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/DouyinFE/semi-design"&gt;semi-design&lt;/a&gt; (SCSS)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pinterest/gestalt"&gt;gestalt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/primefaces/primereact"&gt;primereact&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pivotal-cf/pivotal-ui"&gt;pivotal-ui&lt;/a&gt; (SCSS)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coreui/coreui-react"&gt;coreui-react&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AgnosticUI/agnosticui"&gt;agnosticui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/styling/css-in-js"&gt;CSS-in-JS and Server Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/reactwg/react-18/discussions/110"&gt;Library Upgrade Guide: &amp;lt;style&amp;gt; (most CSS-in-JS libs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackdiary.com/css-in-js-libraries/"&gt;The Most Popular CSS-in-JS Libraries in 2023&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/a-thorough-analysis-of-css-in-js/"&gt;A Thorough Analysis of CSS-in-JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MicheleBertoli/css-in-js"&gt;CSS in JS techniques comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://necolas.github.io/react-native-web/benchmarks/"&gt;react-native-web/benchmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kurtextrem.de/posts/svg-in-js"&gt;Breaking Up with SVG-in-JS in 2023&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ReactNative with Tailwind
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nativewind.dev/"&gt;NativeWind&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://formidable.com/open-source/react-native-zephyr/"&gt;React Native Zephyr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jaredh159/tailwind-react-native-classnames"&gt;tailwind-react-native-classnames&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/react-universal/tailwind"&gt;React Universal Tailwind&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>npm</category>
      <category>css</category>
    </item>
  </channel>
</rss>
