<?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: Kat Marchán</title>
    <description>The latest articles on DEV Community by Kat Marchán (@zkat).</description>
    <link>https://dev.to/zkat</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%2F222724%2F416173bd-1ac1-4c17-b89d-702b68265e52.jpeg</url>
      <title>DEV Community: Kat Marchán</title>
      <link>https://dev.to/zkat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zkat"/>
    <language>en</language>
    <item>
      <title>I replaced htmx with a simple web component</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Fri, 06 Sep 2024 19:01:59 +0000</pubDate>
      <link>https://dev.to/zkat/i-replaced-htmx-with-a-simple-web-component-4bnh</link>
      <guid>https://dev.to/zkat/i-replaced-htmx-with-a-simple-web-component-4bnh</guid>
      <description>&lt;p&gt;(Image credit: &lt;a href="https://www.maicar.com/GML/Ajax1.html" rel="noopener noreferrer"&gt;https://www.maicar.com/GML/Ajax1.html&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I recently posted on Mastodon about how I was using &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;htmx&lt;/a&gt; to much success, and someone rolled into my mentions challenging me on that, and how htmx is actually a pretty heavy dependency considering what I was using it for. They linked me to &lt;a href="https://gomakethings.com/why-not-htmx/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; and everything.&lt;/p&gt;

&lt;p&gt;At first, I was kind of annoyed. I thought I was doing a pretty good job of keeping things lightweight, and htmx had served me well. But then, I put on the hat that I've been trying to wear this whole time when it comes to reinventing the way I do web dev: are my assumptions right? Can I do better?&lt;/p&gt;

&lt;p&gt;So I went ahead and replaced my entire usage of htmx with a tiny, 100-line, vanillajs web component, that I'm going to include in this post in its entirety:&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AjaxIt&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitEvent&lt;/span&gt;&lt;span class="p"&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;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLFormElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&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;beforeEv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:beforeRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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="na"&gt;cancelable&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;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;beforeEv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;beforeEv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultPrevented&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="p"&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:beforeSend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;submitter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLButtonElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;formAction&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&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;try&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/x-www-form-urlencoded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ajax-It&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&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="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:afterRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;injectReplacements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:requestFailed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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;span class="p"&gt;})();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&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;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLAnchorElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:beforeRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:beforeSend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&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;try&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ajax-It&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;request failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:afterRequest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;injectReplacements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it:requestFailed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bubbles&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="na"&gt;composed&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;span class="p"&gt;})();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;injectReplacements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;html&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;mainTargetConsumed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&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;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[id]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]];&lt;/span&gt;
      &lt;span class="k"&gt;for &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;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If we have a parent that's already going to replace us, don't bother,&lt;/span&gt;
        &lt;span class="c1"&gt;// it will be dragged in when we replace the ancestor.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentWithID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentElement&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;closest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[id]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentWithID&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parentWithID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&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="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;replaceWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mainTargetConsumed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&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="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;replaceWith&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;childNodes&lt;/span&gt; &lt;span class="o"&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;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ajax-it&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AjaxIt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You use it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ajax-it&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/some/url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ajax-it&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Any elements with an &lt;code&gt;id&lt;/code&gt; included in the response will be replaced when the response comes back. It works for &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements, too!&lt;/p&gt;

&lt;p&gt;The element works two main ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If your &lt;code&gt;action&lt;/code&gt; or &lt;code&gt;href&lt;/code&gt; includes a hash, the element on or current page with an id matching that hash will be replaced with the contents of the entire response.&lt;/li&gt;
&lt;li&gt;If your returned html contains  elements that themselves have IDs, and those IDs have matches in the current document, those elements will be replaced first (and excluded from the “whole response” replacement above). This is essentially how you do “out of band” swaps (aka &lt;code&gt;hx-swap-oob&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, with some html like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;extra-stuff&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;user-list&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ajax-it&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/users/list#user-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Get users
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ajax-it&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a server response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 1
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 2
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;extra-stuff&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 1
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 2
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ajax-it&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/users/list#user-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Get users
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ajax-it&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your response had been:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 1
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 2
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;extra-stuff&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello, I'm out-of-band&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you would have ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;extra-stuff&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello, I'm out-of-band&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 1
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 2
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;ajax-it&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/users/list#user-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Get users
  &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ajax-it&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...with the &lt;code&gt;id=extra-stuff&lt;/code&gt; swapped out-of-band and the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; swapped normally.&lt;/p&gt;

&lt;p&gt;To maintain idempotency, though, I don't tend to use the hash version of things, and just make sure all my response elements have attached IDs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;user-list&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 1
  &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;user 2
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;extra-stuff&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello, I'm out-of-band&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which would maintain the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; id and make clicking on the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; repeatedly idempotent (as one would expect). In this case, your &lt;code&gt;href&lt;/code&gt; can just be &lt;code&gt;href="/users/list"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also fully progressively enhanced: as long as your &lt;code&gt;action&lt;/code&gt; attribute points to a regular endpoint, things will behave as expected if JS isn't working or fails to load. All you have to look for on the server side is an &lt;code&gt;Ajax-It: true&lt;/code&gt; header, so you can respond with minimal html instead of a full response.&lt;/p&gt;

&lt;p&gt;Huge kudos and credit to &lt;a href="https://leanrada.com/htmz/" rel="noopener noreferrer"&gt;htmz&lt;/a&gt;, which this is largely based on, except I needed to do it with AJAX instead of the iframe trick because I actually needed lifecycle events to do some of the offline trickery I'm doing.&lt;/p&gt;

&lt;p&gt;Anyway cheers. Feel free to use the element in your own stuff! Consider it public domain :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>I was isekaid into developing a minimal-JavaScript online game using The Platform as much as I could and wrote about it 🧙</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Thu, 05 Sep 2024 16:04:24 +0000</pubDate>
      <link>https://dev.to/zkat/i-was-isekaid-into-developing-a-minimal-javascript-online-game-using-the-platform-as-much-as-i-could-and-wrote-about-it-1l1p</link>
      <guid>https://dev.to/zkat/i-was-isekaid-into-developing-a-minimal-javascript-online-game-using-the-platform-as-much-as-i-could-and-wrote-about-it-1l1p</guid>
      <description>&lt;p&gt;(Cover image credit: Ian MacDougall, &lt;a href="https://www.artstation.com/artwork/oOd05w" rel="noopener noreferrer"&gt;https://www.artstation.com/artwork/oOd05w&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Folks have been nudging me to write this for a while on Mastodon, so I thought I'd put together a little series on a fun app I've been working on for a bit, as I build it.&lt;/p&gt;

&lt;p&gt;It's a web-based PWA intended for playing a family of tabletop RPGs that I'm very fond of: &lt;a href="https://www.ironswornrpg.com/" rel="noopener noreferrer"&gt;Ironsworn/Starforged&lt;/a&gt;. These games are designed around single-player gameplay with optional multiplayer rules, and gameplay ends up looking a lot like journaling plus notes. Or, you could say, a lot like a blog publishing platform. And, like blog publishing platforms, a key thing with this game is that you should be able to publish your journals for other players to read and interact with, along with your characters, your worlds, and all sorts of other creations. A community of fictional creativity, posing as a fun little game.&lt;/p&gt;

&lt;p&gt;What's so different about this app? Well, it's meant to have a lot of the bells and whistles that you'd expect from a PWA with serious offline capabilities, but it's built unlike the vast majority of webapps these days, while still having a fully fancy/interactive UI with all the bells and whistles you might be used to seeing in them. And as of right now, it's all done with a ~40kb JavaScript payload.&lt;/p&gt;

&lt;p&gt;Why am I doing things this way? Because I believe the web platform has come a long way, and frontend applications have gotten unnecessarily heavy, slow, and inaccessible to large portions of the web userbase by their over-reliance on legacy technology like React &amp;amp;co: you don't need to ship megabytes of minified JavaScript and wasm to have a really fancy, modern application. The problem? There's very little documentation about &lt;em&gt;how&lt;/em&gt; you actually go about doing this and using the technologies available because so much of the community documentation and education focuses around JS frameworks. So... I'd like to help a bit on that front.&lt;/p&gt;

&lt;p&gt;To give you a sampling of some topics I'll be covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app is an entirely server-side rendered multi-page application using a plain ol' server-side MVC web framework (Phoenix, without LiveView).&lt;/li&gt;
&lt;li&gt;Oh, and even the Lit web components are server-side rendered. No FOUC or loading spinners to be seen.&lt;/li&gt;
&lt;li&gt;It works entirely offline, and without even having a user account. Once you've loaded the site, you're good to go to play the game on the go, and you can sync later if you want.&lt;/li&gt;
&lt;li&gt;If you're online, you get access to real-time, multiplayer editing.&lt;/li&gt;
&lt;li&gt;As much as possible, &lt;strong&gt;use the platform&lt;/strong&gt;: HTML, CSS, and JavaScript (okay I'm cheating a bit here, because I'm using TypeScript and esbuild, but that's just nice and easy).&lt;/li&gt;
&lt;li&gt;A smattering of web components used as-needed, and only to the extent actually needed, instead of using a frontend framework of any sort (goodbye, React, you're not needed anymore)&lt;/li&gt;
&lt;li&gt;Because of this, as mentioned above, the JavaScript bundle is &lt;em&gt;tiny&lt;/em&gt;, currently weighing in at ~40kb, with minimal dependencies.&lt;/li&gt;
&lt;li&gt;A really fun, CouchDB-style entity synchronization system that can handle the split-brain problem and sync conflicts in a reasonable way (read: without just stomping all over older changes).&lt;/li&gt;
&lt;li&gt;Using iframes (!) for things like a dynamic sidebar that you can navigate around without refreshing the current page.&lt;/li&gt;
&lt;li&gt;Very, very aggressive progressive enhancement: As long as you're online, the entire application should be fully usable if JavaScript is disabled or fails (&lt;a href="https://piccalil.li/blog/a-handful-of-reasons-javascript-wont-be-available/" rel="noopener noreferrer"&gt;no, not just because of a handful of nerds who run Lynx&lt;/a&gt;), while still enhancing up to dynamic, interactive behavior.&lt;/li&gt;
&lt;li&gt;Centering accessibility by using what the browser already provides as much as possible. I guess I'll also be figuring ARIA stuff that I'm not very good at.&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;htmx&lt;/a&gt; for HTML-centered in-page dynamicity (without duplicating a bunch of code!) (update: I've replaced all my usage of htmx with &lt;a href="https://dev.to/zkat/i-replaced-htmx-with-a-simple-web-component-4bnh"&gt;a tiny web component in vanilla JS instead&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll be getting pretty specific and technical about one application in particular during this series, rather than using isolated, minimal examples. That way, the stuff you read is essentially all "real world" code with all the complex concerns that entails. So if you're interested in reading more about any of this, like and subscribe! I'll be POSTing Through It™️.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Kat's Mastodon Quickstart for Twitter Users</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Wed, 17 Mar 2021 00:51:34 +0000</pubDate>
      <link>https://dev.to/zkat/kat-s-mastodon-quickstart-for-twitter-users-39db</link>
      <guid>https://dev.to/zkat/kat-s-mastodon-quickstart-for-twitter-users-39db</guid>
      <description>&lt;p&gt;So you like Twitter, but you're curious about Mastodon? You've come to the right place. I'll make this real quick for you, and you can explore further from there, if you want to get more out of it. My setup is pretty straightforward and lets me post to both Twitter and Mastodon as needed, and maintain both communities easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Get an Account
&lt;/h2&gt;

&lt;p&gt;A defining feature of Mastodon is that you can make an account on many different servers, but those servers can talk to each other (aka "Federation"). Picking a server and signing up for it is the first step, but it can be daunting, with so many choices, so let's simplify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You like Open Source: &lt;a href="https://fosstodon.org" rel="noopener noreferrer"&gt;https://fosstodon.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You're queer and you like tech: &lt;a href="https://tech.lgbt" rel="noopener noreferrer"&gt;https://tech.lgbt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You want to see/post art: &lt;a href="https://mastodon.art" rel="noopener noreferrer"&gt;https://mastodon.art&lt;/a&gt; or &lt;a href="https://artalley.social" rel="noopener noreferrer"&gt;https://artalley.social&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You want the most reliable/biggest instance: &lt;a href="https://mastodon.online/" rel="noopener noreferrer"&gt;https://mastodon.online/&lt;/a&gt; (please avoid this one. You'll miss out on some aspects of Mastodon culture by just defaulting here).&lt;/li&gt;
&lt;li&gt;You like Kat and/or want a chill place and a cute domain name: &lt;a href="https://toot.cat" rel="noopener noreferrer"&gt;https://toot.cat&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You want to take more time to make a more informed choice: &lt;a href="https://joinmastodon.org" rel="noopener noreferrer"&gt;https://joinmastodon.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;P.S. You can always move to a new account. Mastodon has a migration feature that leaves a "tombstone" behind and helps you move over to your new account. Don't sweat this choice too much!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Link Mastodon ↔ Twitter
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go &lt;a href="https://crossposter.masto.donte.com.br/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and authenticate with both accounts. It's safe.&lt;/li&gt;
&lt;li&gt;Go &lt;a href="https://crossposter.masto.donte.com.br/user" rel="noopener noreferrer"&gt;here&lt;/a&gt; and enable crossposting both ways (if that's what you want).&lt;/li&gt;
&lt;li&gt;BONUS: Copy my advanced settings: 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h6lvyvrp9hiwfrkct87r.png" rel="noopener noreferrer"&gt;Mastodon -&amp;gt; Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akhu9h4rezgbgy3pxxl2.png" rel="noopener noreferrer"&gt;Twitter -&amp;gt; Mastodon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Get a Mobile Client
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Android: There's an &lt;a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android&amp;amp;gl=US" rel="noopener noreferrer"&gt;Official App&lt;/a&gt;, or &lt;a href="https://play.google.com/store/apps/details?id=com.keylesspalace.tusky" rel="noopener noreferrer"&gt;Tusky&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;iOS: There's an &lt;a href="https://apps.apple.com/us/app/mastodon-for-iphone-and-ipad/id1571998974" rel="noopener noreferrer"&gt;Official App&lt;/a&gt;, or &lt;a href="https://apps.apple.com/hu/app/toot/id1229021451" rel="noopener noreferrer"&gt;Toot!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Windows/macOS/Linux: &lt;a href="https://nicolasconstant.github.io/sengi/" rel="noopener noreferrer"&gt;Sengi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Or, just use the web interface, which is pretty good! It also works as a Progressive Web App, so you can set it up as a "standalone" app!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 4: Make Friends!
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Check out your instance's local and federated timelines for interesting stuff.&lt;/li&gt;
&lt;li&gt;Link this article on Twitter and get your friends to join.&lt;/li&gt;
&lt;li&gt;Follow me on Mastodon: &lt;a href="https://toot.cat/@zkat" rel="noopener noreferrer"&gt;zkat@toot.cat&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Send me a message by @-ing &lt;code&gt;@zkat@toot.cat hello world&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Step 5: Sh*tpost
&lt;/h1&gt;

&lt;p&gt;Do the thing! Welcome to the Fediverse 💞&lt;/p&gt;

&lt;p&gt;If you want a more in-depth guide, check out &lt;a href="https://github.com/joyeusenoelle/GuideToMastodon" rel="noopener noreferrer"&gt;https://github.com/joyeusenoelle/GuideToMastodon&lt;/a&gt;&lt;/p&gt;

</description>
      <category>twitter</category>
      <category>mastodon</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Effective Codes of Conduct</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Wed, 16 Oct 2019 21:10:11 +0000</pubDate>
      <link>https://dev.to/zkat/effective-codes-of-conduct-56aj</link>
      <guid>https://dev.to/zkat/effective-codes-of-conduct-56aj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;It's 2019 and the issue by now seems to be mostly settled: Codes of Conduct, as it turns out, are an important tool for any community to promote inclusion and protect their members from harassment that would otherwise distract them or push them away.&lt;/p&gt;

&lt;p&gt;But what's the actual experience of designing one, and more importantly, enforcing one?&lt;/p&gt;

&lt;p&gt;I was an admin on &lt;a href="https://lgbtq.technology" rel="noopener noreferrer"&gt;lgbtq.technology&lt;/a&gt;, a community Slack for LGBTQ+ members of the tech industry, for over two years. It had a couple of thousand members, with several hundred active across scores of channels. It was (and still is!) a vibrant community that brought together fairly wide representation of individuals.&lt;/p&gt;

&lt;p&gt;In this article, I'll go over the story of how community management evolved in this particular community. Along the way, I'll share various hard-earned lessons myself and the rest of the admin team there learned about how to effectively manage a diverse community -- and how to use policy to make it inclusive.&lt;/p&gt;

&lt;h2&gt;
  
  
  In the Beginning, One Big House Party
&lt;/h2&gt;

&lt;p&gt;It all &lt;a href="https://twitter.com/seldo/status/547097994386886656" rel="noopener noreferrer"&gt;started with a tweet&lt;/a&gt;. And &lt;a href="https://twitter.com/seldo/status/547160599117832192" rel="noopener noreferrer"&gt;just like that&lt;/a&gt;, a new Slack was born.&lt;/p&gt;

&lt;p&gt;I don't think anyone, much less Laurie, really expected the space to take off the way it did, but before he knew it, he was being bombarded with requests to join. The space grew quickly as news spread in the twittersphere, through Laurie's fairly sizeable following and connections in the SF queer scene.&lt;/p&gt;

&lt;p&gt;As with any community, it didn't take long for conflict to start. There was a fundamental misunderstanding between community members: some founding members expected it to be "one big house party" where people were implicitly expected to behave well, but that didn't work out too well. You see, there were people joining who were not like them. They were people of color, or trans people, or bi people, or all sorts of folks that cis, gay, white men were not necessarily used to interacting with.&lt;/p&gt;

&lt;p&gt;Within a couple of weeks of its creation, &lt;a href="https://github.com/lgbtq-technology/lgbtq-technology.github.io/commit/138e1f6a21b2d39f55b67f1441569f038322d50c" rel="noopener noreferrer"&gt;some members drafted a Code of Conduct&lt;/a&gt; based on the flurry of discussions around the topic, and what to do about conflict in this new community. Some people left, but others had already left because of what they'd run into. Lesson learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problems With Code of Conduct Enforcement
&lt;/h2&gt;

&lt;p&gt;Codes of Conduct are not enough to promote a healthy community. Whether they work is a function of the community itself, the processes that have been put in place, and the specific group of people tasked with handling their rules.&lt;/p&gt;

&lt;p&gt;Then the new community ran into the next problem: enforcement.&lt;/p&gt;

&lt;p&gt;For a long time, enforcement was treated as a binary: You either behave "well enough", and you get to stay, or you "violate the CoC" or "anger the wrong person", and next thing you know, you're permanently banned -- and permanent bans they were.&lt;/p&gt;

&lt;p&gt;Having a binary over bans created an "unofficial" way to manage conflict. Many people felt discouraged from reporting violations or "making a fuss" about them because they didn't want to be responsible for someone getting banned. Those on the other end of the report would often react aggressively and defensively when confronted by staff, because being found "guilty" meant they would lose access to a space they found valuable. There was no policy for bringing someone back into a space, after any length of time.&lt;/p&gt;

&lt;p&gt;In reality there was a lot of leeway at the staff level. Many conflicts were resolved by just private messaging someone and having a conversation with them. But any time a conflict became public or enough people made a fuss, the reported person would lash out, and get banned more for their lashing out than their actual behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Experiment: WeAllJS
&lt;/h2&gt;

&lt;p&gt;Ongoing conflicts over community management in lgbtq.technology's &lt;code&gt;#meta&lt;/code&gt; channel eventually reached a point where it became clear us admins needed to try something else. The problem was, the large community we already had wasn't the best place to be experimenting with new ideas.&lt;/p&gt;

&lt;p&gt;So I went and &lt;a href="https://twitter.com/maybekatz/status/762710450127577088" rel="noopener noreferrer"&gt;started a new one&lt;/a&gt;: &lt;a href="https://wealljs.org" rel="noopener noreferrer"&gt;WeAllJS&lt;/a&gt;, a new community Slack for JavaScript developers.&lt;/p&gt;

&lt;p&gt;It was premised on a few ideas I wanted to play with, and eventually bring back to LGBTQ.tech:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Communities could be made for &lt;em&gt;everyone&lt;/em&gt; while still making space for marginalized people.&lt;/li&gt;
&lt;li&gt;Codes of Conduct could be very detailed, and communities could be made to follow them.&lt;/li&gt;
&lt;li&gt;Emphasizing a healthy enforcement process could resolve conflict without resulting in bans or extra conflict.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;WeAllJS had all the bits I wished I could've experimented with in lgbtq.technology. I wrote a new &lt;a href="https://wealljs.org/code-of-conduct" rel="noopener noreferrer"&gt;Code of Conduct&lt;/a&gt; for it, as well as &lt;a href="https://github.com/WeAllJS/wheelie-slack-app" rel="noopener noreferrer"&gt;a custom Slack bot&lt;/a&gt; which added the concept of a "private channel listing" where members could request invites into the channel but not see their contents right off the bat. Also important was the fact that from the very beginning, the admin staff was composed of LGBTQ people, people of color, women and non-binary people.&lt;/p&gt;

&lt;p&gt;The experiment was fairly successful. I think the best part of it turned out to be the enforcement process itself. You'll notice that the WeAllJS CoC &lt;em&gt;leads&lt;/em&gt; with enforcement rules, first of all, but also that those rules include the entire community in its enforcement, and the rules aren't absolute. As it turns out, violations in WeAllJS are commonplace, due to its detailed CoC, but to this day, only a single person has been permanently banned from the space, and that after plenty of warnings and very caring conversations. For an online community with no demographic restrictions on its membership and about 1700 members, over the course of three years, this is an amazing fact, to me.&lt;/p&gt;

&lt;p&gt;Following my report on my experiences over in WeAllJS, lgbtq.technology ended up &lt;a href="https://lgbtq.technology/coc.html" rel="noopener noreferrer"&gt;adopting a similar enforcement process in its Code of Conduct&lt;/a&gt;. It also adopted Wheelie, the Slack bot, as well as the WeAllJS channel system!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion, and Next Steps
&lt;/h2&gt;

&lt;p&gt;So how can you put all this to use in your own communities and open source projects? Having a code of conduct is a given these days, but what else?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Include clear enforcement procedures either in the CoC itself, or somewhere obviously connected to it.&lt;/li&gt;
&lt;li&gt;Customize your code of conduct to be relevant to your community! Don't just grab the Contributor Covenant and call it a day. That borders on irresponsible.&lt;/li&gt;
&lt;li&gt;If you want a starting point, consider the WeAllJS CoC, or &lt;a href="https://npm.im/weallbehave" rel="noopener noreferrer"&gt;WeAllBehave&lt;/a&gt; for your open source projects.&lt;/li&gt;
&lt;li&gt;Consider employing a consultant, preferable a person of color, to look over your CoC and give you more advice about your community.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that just about wraps this up! I hope this article has proven useful and provided some context over the process and concerns surrounding codes of conduct!&lt;/p&gt;

</description>
      <category>inclusion</category>
      <category>codeofconduct</category>
    </item>
    <item>
      <title>I'm the former tech lead for the NPM CLI, and I've been doing FOSS for 10+ years, Ask Me Anything!</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Tue, 03 Sep 2019 22:39:19 +0000</pubDate>
      <link>https://dev.to/zkat/i-m-the-former-tech-lead-for-the-npm-cli-and-i-ve-been-doing-foss-for-10-years-ask-me-anything-4mdi</link>
      <guid>https://dev.to/zkat/i-m-the-former-tech-lead-for-the-npm-cli-and-i-ve-been-doing-foss-for-10-years-ask-me-anything-4mdi</guid>
      <description>&lt;p&gt;Hi! My name's Kat, and I've been an FOSS dev for over a decade. &lt;/p&gt;

&lt;p&gt;I had the opportunity of being, and eventually tech-leading, the NPM CLI team which I was a part of from 2015 until a couple of months ago. I'm the one who wrote things like &lt;code&gt;npx&lt;/code&gt;, &lt;code&gt;npm ci&lt;/code&gt;, and I helped design and add &lt;code&gt;package-lock.json&lt;/code&gt; and much of its behavior to the CLI, and I was the main author of the ~40x+ speedup between &lt;code&gt;npm@4&lt;/code&gt; and &lt;code&gt;npm@5&lt;/code&gt; and later that made the CLI catch up (and often surpass) Yarn and PNPM in performance.&lt;/p&gt;

&lt;p&gt;I also do a lot of Rust these days, and I'm currently on the NuGet client team at Microsoft, the package manager for the .NET ecosystem. I'm also on the core dev team for the &lt;a href="https://github.com/entropic-dev/entropic" rel="noopener noreferrer"&gt;Entropic&lt;/a&gt; client, &lt;code&gt;ds&lt;/code&gt;, trying to build a new, distributed package manager for the JavaScript community!&lt;/p&gt;

&lt;p&gt;AMA!&lt;/p&gt;

</description>
      <category>ama</category>
      <category>npm</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A System for Sustainable FOSS</title>
      <dc:creator>Kat Marchán</dc:creator>
      <pubDate>Tue, 03 Sep 2019 01:52:25 +0000</pubDate>
      <link>https://dev.to/zkat/a-system-for-sustainable-foss-11k9</link>
      <guid>https://dev.to/zkat/a-system-for-sustainable-foss-11k9</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimers&lt;/strong&gt;: This system is still a work in process. I look forward to reading (and incorporating!) your (constructive!) feedback and I hope to evolve this into a fully usable model that anyone can Just Adopt™. I am also not a lawyer. And even if I were, I am not &lt;strong&gt;your&lt;/strong&gt; lawyer. Don't take any of this as legal advice or in any way legally sound. If you want to make sure this is legally sound &lt;strong&gt;for you&lt;/strong&gt;, get &lt;strong&gt;your lawyer&lt;/strong&gt; to look at it. Finally, I'm in no way affiliated with License Zero. I am writing this because I think it's created some great tools to achieve what we're trying to do here.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before you read anything else, note this: This whole thing focuses on &lt;strong&gt;developer tools&lt;/strong&gt; -- it's not intended to be used as-is for things like libraries, databases, or operating systems. It does cover compilers, linters, package managers, bundlers, and all sorts of other such tooling. My hope is that by being more focused on a specific class of software, we can have a better chance of succeeding.&lt;/p&gt;

&lt;p&gt;This isn't to say this model can't apply to other use cases. It's just that I won't cover here the implications for those other cases, and there might be some surprising complexities in implementation for those.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Entire System, Summarized
&lt;/h2&gt;

&lt;p&gt;Since this post is meant to be used as a reference post, here's the entire model in one place. Click through the various links for a detailed explanation of the how and why of each step!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Apply the Parity License to your project.&lt;/li&gt;
&lt;li&gt;Apply the Apache 2.0 License to your project's third-party contributions.&lt;/li&gt;
&lt;li&gt;
Set up a patronage/crowdfunding platform, and make sure it has a tier that grants licenses to sponsors. &lt;/li&gt;
&lt;li&gt;
Apply the Patron License to your project.&lt;/li&gt;
&lt;li&gt;(Optional) Handle additional licensing with other License Zero licenses.&lt;/li&gt;
&lt;li&gt;Read about why do this whole thing in the first place.&lt;/li&gt;
&lt;li&gt;Quit your day job when the above makes enough money for you. Congratulations, you have a working free software business now!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Model, in Detail
&lt;/h2&gt;

&lt;p&gt;The model I'm proposing involves moving our communities back towards a Free Software model -- by which I mean, using so-called Copyleft licensing -- and using that to direct funding towards maintainers in order to compensate them for their labor. This proposed workflow is not fundamentally different from the licensing model used by companies like Red Hat or MongoDB, but it is more specific and adapted to smaller projects maintained by only one or only a couple of devs, and it's also designed to be possible for "devtools" maintainers, a usage gap not covered by most licenses used by those larger open source companies. I'll summarize the entire model in the end, but first, some background on the motivations behind all this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Parity License
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghzb0jiu2u7ddlguwxe5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghzb0jiu2u7ddlguwxe5.png" alt="Image of the parity license rules showing that you're not allowed closed source use." width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Apply the Parity License to your project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first step in this model is using a strong copyleft license -- something like the AGPL, but in this case, I prefer &lt;a href="https://paritylicense.com/" rel="noopener noreferrer"&gt;the Parity License&lt;/a&gt;, due to its closing of the "devtools loophole" -- that is, it imposes the share-alike requirement on anyone using your developer tooling (linters, compilers/transpilers, editors, etc.), not just people "linking" or in the case of the Affero GPL, "using your service".&lt;/p&gt;

&lt;p&gt;To apply the license, simply copy the &lt;a href="https://licensezero.com/licenses/parity" rel="noopener noreferrer"&gt;license text&lt;/a&gt; into a &lt;code&gt;LICENSE-PARITY&lt;/code&gt; file in the root of your tool. If you're using a package manager that recognized &lt;a href="https://spdx.org/licenses/" rel="noopener noreferrer"&gt;SPDX license expressions&lt;/a&gt; (like NPM and Cargo), the ID you're looking for is &lt;code&gt;Parity-6.0.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Considering how many small-team open source projects out there are actually developer tools, I believe this is a critical gap that needs bridging. The Parity License also comes with the benefit of being way more readable to humans, and even includes clear forgiveness for those who weren't aware of the particulars of the license, so long as they start complying with the terms after finding out. Another favorite part about this license for me is that it allows others to license their own software in a more permissive license than Parity itself, so it's not the same kind of "viral" as other copyleft licenses.&lt;/p&gt;

&lt;p&gt;What are we trying to get out of this licensing decision?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make sure everyone in the free software community is able to use our software freely, so long as they are also sharing their software.&lt;/li&gt;
&lt;li&gt;Open the doors to licensing arrangements so people and corporations looking to write proprietary software have to give back in some way.&lt;/li&gt;
&lt;li&gt;Close any usage loopholes that would allow proprietary users to bypass these expectations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Third Party Contributions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnuyt8hr6tksm5j6wry8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpnuyt8hr6tksm5j6wry8.jpg" alt="Image of hand signing a legal document" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Apply the Apache 2.0 License to your project's third-party contributions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, once we start dealing with copyleft with the intention of granting proprietary licenses, we come up to probably the trickiest part of this entire model: making sure that people can still contribute to your project.&lt;/p&gt;

&lt;p&gt;This is the part where I can only give vague advice, as a non-lawyer, but this is my own personal understanding is that every single contributor to your project must agree to one of several alternatives, if you're to be able to pull off this workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agree to license their own changes under a permissive license (like MIT or Apache 2.0).&lt;/li&gt;
&lt;li&gt;Agree to grant you the ability to grant private licenses to third parties, while retaining their own copyright.&lt;/li&gt;
&lt;li&gt;Assign their copyrights to you.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which one you pick is ultimately up to you, but you &lt;strong&gt;must&lt;/strong&gt; make sure that this is made clear and explicit, for example, through note on your &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;, or &lt;a href="https://en.wikipedia.org/wiki/Contributor_License_Agreement" rel="noopener noreferrer"&gt;a Contributor License Agreement&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the purposes of this blog post, we'll go with the first alternative: having third-party contributions licensed under &lt;a href="https://www.apache.org/licenses/LICENSE-2.0" rel="noopener noreferrer"&gt;Apache 2.0&lt;/a&gt;. Apache 2.0 is a VERY permissive license, much like the MIT and BSD0 license, except it also has language granting patent permissions, which is considered pretty important nowadays.&lt;/p&gt;

&lt;p&gt;So go ahead and copy &lt;a href="https://www.apache.org/licenses/LICENSE-2.0.txt" rel="noopener noreferrer"&gt;the text of the license&lt;/a&gt; and put it in a file called &lt;code&gt;LICENSE-APACHE&lt;/code&gt; in your project's root folder. If you're using a package manager with a &lt;code&gt;license&lt;/code&gt; field, the new SPDX expression for it would be &lt;code&gt;Parity-6.0.0 AND Apache-2.0&lt;/code&gt;, so add that in as well.&lt;/p&gt;

&lt;p&gt;If you want to go the other routes, or you still want to use a CLA even for the Apache licensing, you can go over to &lt;a href="http://www.harmonyagreements.org/index.html" rel="noopener noreferrer"&gt;Project Harmony&lt;/a&gt; for some templates, and even an &lt;a href="http://selector.harmonyagreements.org/" rel="noopener noreferrer"&gt;Agreement Selector&lt;/a&gt;. You can also set up a &lt;a href="https://colineberhardt.github.io/cla-bot/" rel="noopener noreferrer"&gt;CLA bot&lt;/a&gt; for your GitHub repository to automate the process of signing these.&lt;/p&gt;

&lt;p&gt;But wait, you ask, this whole exercise is about sustainability and software freedom. Why do contributors have to give up so much for no reward? My answer to that is that you shouldn't accept nontrivial patches from contributors without some kind of compensation. Once you're making money from this whole setup, I believe it is the ethical thing to do to offer some part of that to contributors that submit nontrivial changes to your project. Going further, regular contributors should be made full collaborators on the project and you should split the proceeds from your sponsorships and license agreements more fairly with them. The specifics are up to you -- and them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Crowdfunding Platforms
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnd47plnfoi70x671g35x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnd47plnfoi70x671g35x.jpg" alt="Lanterns at night floating into the sky" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Set up a patronage/crowdfunding platform, and make sure it has a tier that grants licenses to sponsors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You have a few choices here, depending on your team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you're the only core dev on your project, I recommend signing up for &lt;a href="https://github.com/sponsors" rel="noopener noreferrer"&gt;GitHub Sponsors&lt;/a&gt; or &lt;a href="https://patreon.com" rel="noopener noreferrer"&gt;Patreon&lt;/a&gt; if that's not available (as of this writing, Sponsors is still in beta and sponsors accounts are being granted in a trickle as GH figures out the kinks).&lt;/li&gt;
&lt;li&gt;If you're on a team, I recommend using something like &lt;a href="https://opencollective.com" rel="noopener noreferrer"&gt;OpenCollective&lt;/a&gt; or similar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should set up some basic tiers with rewards you're willing to give your sponsors. This can be things like provide X number of hours of support, adding them to a "thank you" list in your README, or even have their logo emblazoned on your project site. Be creative!&lt;/p&gt;

&lt;p&gt;The most important thing here, though, is to decide which of the tiers will grant people a license to use your software in closed source scenarios. Since this essentially will amount to a subscription, I recommend you aim at a reasonable number here, taking that into account.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Patron License
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uybf9pyw50rjgxhrfns.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uybf9pyw50rjgxhrfns.jpg" alt="One-cent coins spilling out of a jug" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Apply the Patron License to your project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go ahead and download &lt;a href="https://blog.licensezero.com/2019/05/24/patron-license.html" rel="noopener noreferrer"&gt;the Patron License&lt;/a&gt; and fill it out, then stick it into &lt;code&gt;PATRONAGE.md&lt;/code&gt; in your project root.&lt;/p&gt;

&lt;p&gt;This bit of legalese is what will grant individual subscription licenses to anyone who wants to use your software for proprietary purposes. It's important to note that these will be &lt;strong&gt;single developer&lt;/strong&gt; licenses, and will only last so long as their sponsorship does.&lt;/p&gt;

&lt;p&gt;But what if you want to grant bulk licenses to someone, or an entity? Or let a friend or friendly project have a one-off blanket waiver to the terms of your license? That's where additional licensing comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Additional Licensing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81tc00zwaptvslk4bqkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81tc00zwaptvslk4bqkk.png" alt="License Zero logo" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Handle additional licensing with other License Zero licenses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, there's a couple of important licensing scenarios here I haven't covered yet. What do you do if you just want to grant a one-off waiver to a friend or collaborator? What about selling single-purchase licenses? Or bulk enterprise licenses to enterprises? Yes, in that case, you'll need more specialized licensing.&lt;/p&gt;

&lt;p&gt;Some of these use-cases are already covered by &lt;a href="https://licensezero.com/" rel="noopener noreferrer"&gt;the License Zero tools&lt;/a&gt; so I recommend you head on over and install their tool and use it to &lt;a href="https://licensezero.com/licenses/waiver" rel="noopener noreferrer"&gt;grant waivers&lt;/a&gt; and &lt;a href="https://licensezero.com/licenses/private" rel="noopener noreferrer"&gt;private licenses&lt;/a&gt; to your heart's content!&lt;/p&gt;

&lt;p&gt;Again, just to restate it: I am in no way affiliated with License Zero and all of this is purely because I think their tooling and licensing is the best means to the ends I'm trying to achieve here. I'm more than happy to give them a boost because of that!&lt;/p&gt;

&lt;p&gt;Anyway that's that! You're done! Go forth and create awesome things! Happy hacking!&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Background
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk69dmueu9v9nvmpmm1j4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk69dmueu9v9nvmpmm1j4.jpg" alt="Extended hand with palm up" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rise of Free and Open Source software, the Cult of GitHub, the ecosystems spawned by language-centered package managers, and corporate acceptance and participation in Open Source have created an incredibly unsustainable situation where freely sharing one's source code and work has become the norm, rather than the exception, for vast communities of developers, be it professionals, hobbyists, or students. So much so, that we've come to expect availability of source code in a multitude of cases where closed source was previously normalized. The dream of Open Source (as defined by the Open Source Initiative), is practically achieved.&lt;/p&gt;

&lt;p&gt;But where does that leave us?&lt;/p&gt;

&lt;p&gt;In getting swept away by the thrill of popularity, the excitement of other people willingly using our code to achieve great things, I believe we have gotten a bit ahead of ourselves and forgotten to care about what's most important: our own selves and our well-being.&lt;/p&gt;

&lt;p&gt;Free Software, as defined by the FSF and other Free Software advocates, had the answer to some of this: by creating a world of free software, where certain rights were reciprocally guaranteed, we would be able to ensure the long-term sustainability of our communities and be able to push away corporate exploitation. This was achieved through software licensing that was in many ways hostile to corporate interests, but friendly to the communities developing the software: "I'll scratch your back, so long as you scratch mine."&lt;/p&gt;

&lt;p&gt;Alas, the OSI and its efforts annihilated much of the then-rising popularity of this model, and I believe this was to the immense detriment of our communities.&lt;/p&gt;

&lt;p&gt;You see, while many developers (and the corporations that employ them) are happily using software freely available through GitHub, NPM, Ruby Gems, and various other distribution methods, maintainers are hitting their breaking point in what's become a bit of an open source sustainability crisis: maintainers are burning out, overwhelmed by the demands of an increasingly large community on a project they are usually only allowed to work on in their "spare" time, while simultaneously being punished for any attempts at implementing more sustainable models, such as the recent advertisement-based funding controversy.&lt;/p&gt;

&lt;p&gt;For some reason, maintainers are expected to work on what is essentially a full time job, for free, or to rely on basic charity that ultimately only benefits those who are popular enough, such as crowdfunding and sponsorship platforms. Remember that work is work, even when you're doing open source, and that pressuring people into doing work for free for you is ultimately unethical.&lt;/p&gt;

&lt;p&gt;After some time thinking and examining various possibilities, I believe I've come up with one way to change this for the better: A way for maintainers to be able to justify spending their time on their projects while continuing to ensure the free availability of source code, and without relying on the charity of strangers to do so. That is, going from charity into a fair deal.&lt;/p&gt;

&lt;h1&gt;
  
  
  CHANGELOG
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Updated the former CLA section to instead recommend that third-party contributions be licensed under Apache-2.0 or a similar permissive license to make the whole process easier. CLA documentation stayed in place for those interested in going that route.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>devtools</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
