<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>HipsterBrown&#39;s Training Data</title>
  <subtitle>A collection of thoughts and explorations from the mind of HipsterBrown.</subtitle>
  <link href="" rel="self"/>
  <link href="https://hipsterbrown.com"/>
  <updated>Wed, 01 Apr 2026 02:06:00 GMT</updated>
  <id>https://hipsterbrown.com</id>
  <author>
    <name>Nicholas Hehr</name>
    <email>headhipster@hipsterbrown.com</email>
  </author>
  
  <entry>
    <title>Generating Open Graph Images in 11ty</title>
    <link href="https://hipsterbrown.com/training-data/generating-open-graph-images-in-11ty/"/>
    <updated>Wed, 01 Apr 2026 02:06:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/generating-open-graph-images-in-11ty/</id>
    <category term="til"/>
    <content type="html">&lt;p&gt;When I working on &lt;a href=&quot;https://hipsterbrown.com/projects/personal-site/&quot;&gt;redesigning my website&lt;/a&gt;, one of the big features I wanted to add was Open Graph (OG) images. These always seemed like such a flex to see from personal blogs and really made those links stand out in every social site that supported them.&lt;/p&gt;
&lt;p&gt;I had a few main criteria to cover when adding support for this feature:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;images should be generated automatically for each content collection&lt;/li&gt;
&lt;li&gt;the process should be integrated into the 11ty build system I use for my site&lt;/li&gt;
&lt;li&gt;it should be efficient enough to run anywhere, i.e. my laptop or GitHub Actions&lt;/li&gt;
&lt;li&gt;the images should match the theme of my site, including fonts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I knew very little about how OG images are implemented outside of some special &lt;code&gt;meta&lt;/code&gt; tags that go into the head of the HTML. Thankfully those tags have become fairly standardized over the years.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Generating Open Graph Images in 11ty&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://hipsterbrown.com/training-data/generating-open-graph-images-in-11ty/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://hipsterbrown.com/og/generating-open-graph-images-in-11ty.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:type&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:description&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Integrate automatic OG image generation into your 11ty site&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of those tags needs to be included in the head of each page that wants the social glow up treatment. So I created the following template partial to contain the small amount of logic required to use the correct content for the relevant tags:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Open Graph Meta Tags --&gt;&lt;/span&gt;
{%- if type -%}
  {%- assign ogImageUrl = &#39;/og/&#39; | append: page.fileSlug | append: &#39;.png&#39; -%}
{%- elsif contentType == &quot;project&quot; -%}
  {%- assign ogImageUrl = &#39;/og/projects/&#39; | append: page.fileSlug | append: &#39;.png&#39; -%}
{%- else -%}
  {%- assign ogImageUrl = &#39;/og/&#39; | append: ogImage | default: &#39;default.png&#39; | append: &#39;&#39; -%}
{%- endif -%}

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ title | escape }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:description&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ description | escape }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:type&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:url&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ canonicalUrl }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://hipsterbrown.com{{ ogImageUrl }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:width&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:height&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;630&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;og:image:alt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{{ title | escape }}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with the main meta tags, I’ve also included extra metadata about the image to help with displaying them on each site.&lt;/p&gt;
&lt;p&gt;To actually generate the images for the landing pages (home, training data index, project index, etc) and each page in a content collection, I created a Node.js script that used the &lt;a href=&quot;https://npmx.dev/package/satori&quot;&gt;satori package&lt;/a&gt; to generate an SVG “card” design from an HTML &amp;amp; CSS template and render it to a PNG using &lt;a href=&quot;https://npmx.dev/package/@resvg/resvg-js&quot;&gt;Resvg&lt;/a&gt;. Satori allows me to use flex box layout, embedded custom fonts, and my site’s theme colors in the generated design without having to spin up a headless browser, like folks have done for this feature in the past. I opted out of using the JSX syntax for relative simplicity and efficiency when running the script, so the template can be a bit confusing to follow at first:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; accent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accent&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ink
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; label &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LABEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateStr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; date
    &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLocaleDateString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;en-US&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;numeric&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;long&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;numeric&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; titleSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;70&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;56&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;72&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Limit description to 150 characters and 2 lines&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; truncatedDesc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; description
    &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;150&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;...&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;630&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;flexDirection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;column&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;justifyContent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;space-between&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0px&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;fontFamily&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Epilogue&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;boxSizing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;border-box&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Top accent bar&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;100%&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; accent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Main content&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;flexDirection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;column&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;justifyContent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;space-between&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;56px 80px 64px&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
              &lt;span class=&quot;token comment&quot;&gt;// Type label&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;fontFamily&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Syne&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; accent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;letterSpacing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0.12em&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;textTransform&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;uppercase&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; label&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;flexDirection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;column&#39;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; titleSize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontFamily&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;InstrumentSerif&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ink&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;lineHeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;960&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// Description (optional)&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;truncatedDesc &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;lineHeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;960&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;marginTop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;WebkitLineClamp&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;WebkitBoxOrient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;vertical&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; truncatedDesc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token comment&quot;&gt;// Title&lt;/span&gt;
              &lt;span class=&quot;token comment&quot;&gt;// Footer row&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;justifyContent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;space-between&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token literal-property property&quot;&gt;alignItems&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex-end&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontFamily&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Syne&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;fontWeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                          &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ink&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;HipsterBrown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COLOR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text3 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dateStr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Looks reminiscent of the early days of React before everyone used JSX by default. 😆&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://hipsterbrown.com/og/generating-open-graph-images-in-11ty.png&quot; alt=&quot;blog post open graph image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;That function is called with data for each content type or static page:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateForPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; slug&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;og&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;slug&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.png&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; png &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderPng&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buildCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; outputPath
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateStatic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; png &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderPng&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;buildCard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; outputPath
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script gathers all the posts, along with their front-matter data using &lt;a href=&quot;https://npmx.dev/package/gray-matter&quot;&gt;gray-matter&lt;/a&gt;, then iterates over each one to generate it’s associated image into the site build directory.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;training-data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.md&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fileNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;training-data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; posts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;matter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;draft&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; slug &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;training-data/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.md&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; slug&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/HipsterBrown/hipsterbrown.com/blob/main/scripts/generate-og-images.mjs&quot;&gt;Full OG generation script source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Integration into 11ty comes in the &lt;code&gt;eleventy.after&lt;/code&gt; lifecycle hook; once all the pages are generated.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eleventy.after&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; spawn &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;child_process&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; proc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;node&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;scripts/generate-og-images.mjs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;close&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;OG generation failed with code &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;code&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generating the pages and images for my site takes about 6 seconds in CI, so I’m currently keeping the images for content collections out of git. This removes the concern of keeping track of which images have been generated already or regenerating them all manually if I decide to update the design.&lt;/p&gt;
&lt;p&gt;Overall, I’m happy with how quick and easy this works once it was all in place. I’m looking forward to playing around with the design a bit more in the future, but it’s in a good enough place for now.&lt;/p&gt;
&lt;p&gt;If you’ve added this feature to your site in some other way, reach out and let me know how you did it.&lt;/p&gt;
&lt;p&gt;Happy Building!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Laundry Alerts with Home Assistant</title>
    <link href="https://hipsterbrown.com/training-data/laundry-alerts-with-home-assistant/"/>
    <updated>Thu, 19 Mar 2026 03:16:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/laundry-alerts-with-home-assistant/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Yet another project I put off due to a compulsion to build it all from scratch; my personal case of not invented here syndrome.&lt;/p&gt;
&lt;p&gt;TL;DR Skip to the bottom for my Home Assistant automation setup to get notifications when the laundry is done.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;In my home, my laundry machines live in the basement. This is great for keeping the noise, heat, and general mess of gathering dirty clothes away from the rest of the house. However, it&#39;s bad for my penchant to forget when it&#39;s &amp;quot;out of sight, out of mind.&amp;quot; This leads to soggy or wrinkled clothes that should have been put away sooner and frustration with myself. I&#39;ve tried setting timers for myself, based on the expected completion time for the load; however, the machines estimate about as well as software engineers (wrong just enough to be annoying over time). Also, timers are easy to dismiss and forget in the moments after. I really needed something to notify me when either machine was done with its current load. It would be even better if it could notify anyone at home about this as well.&lt;/p&gt;
&lt;h2&gt;Early Attempts&lt;/h2&gt;
&lt;p&gt;As a programmer, self-proclaimed &amp;quot;maker&amp;quot;, and generally frugal person, I tend to want to build a solution myself when it seems possible and none of the off-the-shelf solutions fit within my values of ownership and privacy. My initial plan was to set up a movement or vibration sensor wired up to a microcontroller placed on the machines somewhere to track the laundry activity. Then I fell into a rabbit hole of improving the developer tooling around the project rather than implementing the project itself (classic). I also wasn&#39;t sure where to report the data or how to notify myself based on the data. I eventually moved on to more pressing needs within my home.&lt;/p&gt;
&lt;p&gt;Then, after starting my role as &lt;a href=&quot;https://viam.com/&quot;&gt;Viam&lt;/a&gt;, I got the opportunity to try solving this annoyance as a proof-of-concept for work. This time it would involve a sensor wired up to a Raspberry Pi that captured data to Viam and allowed me to quickly visualize it in a dashboard. Having solved that part of the problem so easily, I decided to shift the goal posts to include using machine learning for recognizing loads and detect anomalies in the behavior of the washer &amp;amp; dryer. This was far beyond my current experience with ML, so the interest lapsed and the problem went unsolved yet again.&lt;/p&gt;
&lt;h2&gt;Catalyst&lt;/h2&gt;
&lt;p&gt;These days, I have two young kids and no real time to tinker extensively with hand-crafted solutions for problems that people have already solved and documented. It was during my parental leave that I finally committed to just getting the damn thing done. While I was going through the long-pending maintenance of my Home Assistant setup, I realized that this was the perfect scenario for the open source home automation community.&lt;/p&gt;
&lt;p&gt;After doing some research and planning with Claude, I settled on the initial approach: a power-monitoring smart plug for the washer and a wireless vibration sensor for the dryer. Since my dryer is fully electric, there was no viable smart plug that could support the high-voltage connection. As for connectivity, the smart plug (made by Shelly) supported Matter and the vibration sensor used Zigbee. I prefer Matter for most of my smart home devices these days for the sake of ecosystem portability and offline availability, but the same USB adapter connected to my &lt;a href=&quot;https://www.home-assistant.io/green/&quot;&gt;Home Assistant Green&lt;/a&gt; provides Zigbee along with Thread and Matter due to shared radio frequency for the protocols.&lt;/p&gt;
&lt;p&gt;As part of my research and a big influence into choosing a smart plug for monitoring the washer, I also learned about a Home Assistant community integration called &lt;a href=&quot;https://github.com/3dg1luk43/ha_washdata&quot;&gt;WashData&lt;/a&gt;. This plugin abstracted over the power monitoring data from the smart plug to display it as a device (or entity) within Home Assistant, including activity states (starting, running, completing, idle), progress, and detected load type. It enabled this metadata through a learned heuristic by manually capturing a few &amp;quot;episodes&amp;quot; of data for each load type you expect to run. This sort of setup also makes it conducive to supporting other appliances with varying power draw like dryers, diswashers, air fryers, and heat pumps.&lt;/p&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;You&#39;ve made it through the backstory to the actual recipe you came for: the automations.&lt;/p&gt;
&lt;p&gt;My original goal was to get a notification when my washer or dryer completed a load, with the stretch goal of being able to inform anyone at home about that status. I&#39;m happy to say I accomplished both fairly well.&lt;/p&gt;
&lt;p&gt;Because I have the Home Assistant companion app on my phone, I could easily add an action to send a push notification through the app when the laundry was done. This has a beneficial side-effect from &amp;quot;Apple Intelligence&amp;quot; highlighting this notification as high priority, which makes it hard to ignore.&lt;/p&gt;
&lt;p&gt;In order to inform the rest of the house, I used the local text-to-speech integration to make an announcement through the HomePod Mini in the kitchen! I&#39;m thoroughly impressed by the performance and availability of such a feature native to Home Assistant.&lt;/p&gt;
&lt;p&gt;Here is the washer notification config:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Washer Done Notification
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; turned_off
    &lt;span class=&quot;token key atrule&quot;&gt;device_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 35386f2b898ea435eb3020651caacbce
    &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 44016cbcbca307fc01bbe4b158912d56
    &lt;span class=&quot;token key atrule&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; binary_sensor
    &lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; device
    &lt;span class=&quot;token key atrule&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.persistent_notification
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Laundry is ready to be changed to the dryer&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Laundry Done
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.mobile_app_kitchen_hub
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Washer is ready to be changed over&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Laundry Ready
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.mobile_app_nick_s_phone_pro
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Laundry is ready
      &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Change over laundry
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.volume_set
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;volume_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tts.speak
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tts.piper
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;media_player_entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
          &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Washer laundry is done
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.volume_set
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;volume_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; single&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keen-eyed readers may have noticed the &amp;quot;Kitchen Hub&amp;quot; device being notified as well. That&#39;s a separate project that I&#39;ll be writing about soon.&lt;/p&gt;
&lt;p&gt;Here are the two automation configs to track the dryer state based on the vibration sensor and notify me when it is complete:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Start Dryer Cycle
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; motion
    &lt;span class=&quot;token key atrule&quot;&gt;device_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 7e1ee4370e56ac4372e3024b45974639
    &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 21d145753fe00339f165648f7461a928
    &lt;span class=&quot;token key atrule&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; binary_sensor
    &lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; device
    &lt;span class=&quot;token key atrule&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_boolean.turn_on
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_boolean.dryer_running
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; single&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dryer Load Complete
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no_motion
    &lt;span class=&quot;token key atrule&quot;&gt;device_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 7e1ee4370e56ac4372e3024b45974639
    &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 21d145753fe00339f165648f7461a928
    &lt;span class=&quot;token key atrule&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; binary_sensor
    &lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; device
    &lt;span class=&quot;token key atrule&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
    &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_boolean.dryer_running
    &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;on&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_boolean.turn_off
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_boolean.dryer_running
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.persistent_notification
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Time to take out the laundry from the dryer.
      &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dryer Complete
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.mobile_app_nick_s_phone_pro
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Time to get the laundry from the dryer
      &lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dryer Done&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; notify.mobile_app_kitchen_hub
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dryer is done&lt;span class=&quot;token tag&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.volume_set
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;volume_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tts.speak
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tts.piper
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;media_player_entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
          &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dryer laundry is done
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.volume_set
        &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; media_player.kitchen
        &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;volume_level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; single&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap It Up&lt;/h2&gt;
&lt;p&gt;Overall, I&#39;m glad I finally got this project done using whatever means necessary. I&#39;m finding it genuinely useful, and my family is delighted by the laundry announcements that occasionally ring from the kitchen. It&#39;s taught me to embrace the quirks with home automation and let me explore more interesting ways to use Home Assistant. I&#39;d still like to build the complete DIY approach someday, but it&#39;s nice knowing I have working solution in the meantime.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Alloy SDK for Pebble</title>
    <link href="https://hipsterbrown.com/training-data/the-alloy-sdk-for-pebble/"/>
    <updated>Sun, 15 Mar 2026 18:08:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/the-alloy-sdk-for-pebble/</id>
    <category term="link"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Moddable is thrilled to be collaborating with Pebble to reimagine the watch developer experience using standard JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It feels like my journey with JavaScript hardware is coming full circle. The &lt;a href=&quot;https://www.kickstarter.com/projects/getpebble/pebble-e-paper-watch-for-iphone-and-android&quot;&gt;OG Pebble&lt;/a&gt; was my first foray into developing applications for a platform other than the web, which I had only been doing for a year or two at that time anyway. From building watch faces in C with some JS integration code in the mobile app to full JS-driven experiences and early on-device JS runtime support really shaped my perspective on great developer experiences and what’s possible with hardware. It led to some of my first public speaking opportunities at local meetups in Orlando and New York; eventually leading to meeting the &lt;a href=&quot;https://github.com/tessel/project&quot;&gt;Tessel&lt;/a&gt; team at an open source collaboration event, where I saw a whole new entrypoint into JS-powered hardware.&lt;/p&gt;
&lt;p&gt;From Tessel to participating in &lt;a href=&quot;https://ecma-international.org/technical-committees/tc53/&quot;&gt;TC53&lt;/a&gt;, where I got to know the folks from &lt;a href=&quot;https://moddable.com/&quot;&gt;Moddable&lt;/a&gt;. After Tessel dissolved, I found myself wanting a similar developer experience for the Moddable SDK and associated tooling: plug an MCU in, run a couple CLI commands, and start pushing code to the device. This led to building &lt;a href=&quot;https://xs-dev.js.org/&quot;&gt;xs-dev&lt;/a&gt; and pushing the boundaries of my favorite language on devices yet again.&lt;/p&gt;
&lt;p&gt;I told Peter Hoddie when I saw the &lt;a href=&quot;https://moddable.com/moddable-four&quot;&gt;Moddable Four&lt;/a&gt; (in 2023) that it reminded me so much of the experience when using the first e-paper Pebble. It&#39;s incredible to see that dream become a reality.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All sensors are available through ECMA-419 Sensor APIs. The high-level network APIs use web standards; the low-level networking APIs use ECMA-419 standards.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is especially exciting, after years of standards work, to see such a beloved devices adopting these APIs. The peak era of &lt;a href=&quot;https://nodebots.io/&quot;&gt;Nodebots&lt;/a&gt; has come and gone, but there&#39;s potential for a revival with a new generation of developers being exposed to using familiar JavaScript patterns running on their wrists.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Productivity Theater</title>
    <link href="https://hipsterbrown.com/training-data/productivity-theater/"/>
    <updated>Thu, 12 Mar 2026 02:44:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/productivity-theater/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Every few years, I find myself in the same loop. A new tool appears, promising to finally make me efficient. I descend into the rabbit hole: tweaking, optimizing, configuring; emerging some time later with a system that’s marginally better than what I had before and a stack of browser tabs I’ll never close. I went through it with my Neovim config, then with Obsidian and the promise of a perfect personal knowledge graph, then with home automation and my self-hosted homelab. Each time, the pull felt different. Each time, the pattern was identical.&lt;/p&gt;
&lt;p&gt;The latest act is agentic coding tools, and I can see myself peering down at the edge of the rabbit hole yet again.&lt;/p&gt;
&lt;p&gt;I held out on AI coding tools for a while. This was partially due to the lack of good Neovim integrations, in addition to the poor results I was seeing from other devs’ use of them. Then the models got better, and by the time Claude Code picked up steam, I was already using the chat app to generate snippets, copy them into my program, and returning to the app to paste in the error of what didn’t work. The CLI kept me in the terminal and had the codebase as context; it felt like a natural next step. But, of course, it can’t stop there.&lt;/p&gt;
&lt;p&gt;The hype machine churns on. Now there are conductors, skills, sub-agents, spec frameworks, Ralph loops, and a whole philosophy industry springing up around how to best direct your headless workforce. It has the same energy as the peak frontend fatigue years. The pace is frantic mixed with an anxiety that you’re not using your token limit to its peak efficiency every second of the day. It follows us away from the keyboard; urging us to check in on our phones and fire off a couple more remote prompts on the go.&lt;/p&gt;
&lt;p&gt;What I’m trying not to do is confuse movement for progress. I’m not going to put my head in the sand and wait until the dust settles because it just won’t. So I’ve settled into a workflow that works for me. It’s not glamorous, but it feels good enough. I don’t need an always-on agent opening PRs while I sleep. I don’t need a leaderboard gamifying my token usage. I’m okay playing the role of the senior engineer who stays in the loop to bring taste and judgment to the process, rather than reacting to semi-autonomous outputs after the fact.&lt;/p&gt;
&lt;p&gt;I’ll still read what’s new. I’ll still tinker when something genuinely looks interesting and try to remember to write about it here. This is also where I ended up with my previous interests. I still enjoy using Neovim, collecting thoughts in Obsidian, adding sensors to my home, and deploying services to my homelab. It’s now that I recognize the toil on the way to find peace in these areas; toil I’d like to avoid for this next new and shiny thing.&lt;/p&gt;
&lt;p&gt;In the meantime, I’ll sit back in my seat and observe the productivity theater for the show that it is.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Motivated to publish again</title>
    <link href="https://hipsterbrown.com/training-data/motivated-to-publish-again/"/>
    <updated>Tue, 10 Mar 2026 01:59:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/motivated-to-publish-again/</id>
    <category term="note"/>
    <content type="html">&lt;p&gt;Feels good to want to write and publish again. Parental leave is definitely not a vacation, but it’s provided some space to think away from work to explore some ideas aided by my convenient pair programmer just a quick &lt;code&gt;claude&lt;/code&gt; away.&lt;/p&gt;
&lt;p&gt;In the past, I was hindered by the dated look of the site and my publishing workflow requiring being at my laptop. Now I feel as refreshed as the updated design and have figured out ways to reduce the friction to go from thought to draft to live from whatever device I have handy.&lt;/p&gt;
&lt;p&gt;I’m looking forward to sharing more and often; from quick notes like this to daily learning and links to long form essays of sorts. I hope you enjoy this “training data” as much as the bots. 🤖&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Helpers in Home Assistant</title>
    <link href="https://hipsterbrown.com/training-data/helpers-in-home-assistant/"/>
    <updated>Tue, 10 Mar 2026 00:17:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/helpers-in-home-assistant/</id>
    <category term="til"/>
    <content type="html">&lt;p&gt;TIL about helpers in Home Assistant. I had heard about them briefly before to support pre-configured timers that could be placed on dashboards. But I didn’t know the extent of their use.&lt;/p&gt;
&lt;p&gt;I ended up using one to create a “Dropdown” to hold state for cycling a pattern of lights for my bathroom vanity based on the press of a Zigbee remote button. It seems like helpers are bits of state you can define in your Home Assistant system to use for all sorts of triggers, actions, scripts, etc. I enjoyed building up this automation because it could all by done using the UI editor, even though I was given a head start with a YAML config from Claude. It felt trivial to accomplish it all from just my phone.&lt;/p&gt;
&lt;p&gt;The script I ended up with:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cycle Bathroom Lights
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Cycle the bathroom lights patterns
&lt;span class=&quot;token key atrule&quot;&gt;triggers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;device_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 61c9e2xxxxxxxxxxxxxxxxx
    &lt;span class=&quot;token key atrule&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; zha
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; remote_button_short_press
    &lt;span class=&quot;token key atrule&quot;&gt;subtype&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; right
    &lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; device
&lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; script.turn_off
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; script.party_mode_loop
    &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;cycle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
    &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.select_next
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;choose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
            &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
            &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yellow
        &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;190&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;area_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bathroom
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
            &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
            &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white
        &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;brightness_pct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;area_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bathroom
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
            &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
            &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rainbow
        &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.first_vanity_light_light
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.second_vanity_light_light
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;220&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.third_vanity_light_light
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.fourth_vanity_light_light
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.fifth_vanity_light_light_2
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
            &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
            &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue
        &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;rgb_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;area_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bathroom
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
            &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
            &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; party
        &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; script.turn_on
            &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; script.party_mode_loop
            &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; single&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bonus &lt;code&gt;Party Mode&lt;/code&gt; light cycling:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Party Mode Loop
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; restart
&lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state
          &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; input_select.bathroom_light_pattern
          &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; party
      &lt;span class=&quot;token key atrule&quot;&gt;sequence&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;parallel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;hs_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ (repeat.index * 30) % 360 }}&quot;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
              &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.first_vanity_light_light
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;hs_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ (repeat.index * 30 + 72) % 360 }}&quot;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
              &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.second_vanity_light_light
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;hs_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ (repeat.index * 30 + 144) % 360 }}&quot;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
              &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.third_vanity_light_light
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;hs_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ (repeat.index * 30 + 216) % 360 }}&quot;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
              &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.fourth_vanity_light_light
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;hs_color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{{ (repeat.index * 30 + 288) % 360 }}&quot;&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.turn_on
              &lt;span class=&quot;token key atrule&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;entity_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light.fifth_vanity_light_light_2
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;milliseconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Rainbow wave effect through lights
&lt;span class=&quot;token key atrule&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mdi&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;party&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;popper&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>Islands on the Rails</title>
    <link href="https://hipsterbrown.com/training-data/islands-on-the-rails/"/>
    <updated>Fri, 14 Oct 2022 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/islands-on-the-rails/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Given the renewed conversation around &lt;a href=&quot;https://jasonformat.com/islands-architecture&quot;&gt;&amp;quot;Islands Architecture&amp;quot;&lt;/a&gt;, related to the JS community&#39;s interest in frameworks like &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://iles-docs.netlify.app/&quot;&gt;iles&lt;/a&gt; as well as libraries like &lt;a href=&quot;https://is-land.11ty.dev/&quot;&gt;Eleventy&#39;s &lt;code&gt;&amp;lt;is-land&amp;gt;&lt;/code&gt; element&lt;/a&gt;, I thought I would reflect on how I&#39;ve been working in a &lt;a href=&quot;https://guides.rubyonrails.org/index.html&quot;&gt;Ruby on Rails&lt;/a&gt; application that&#39;s been using this architecture since 2018; followed by how I think it can continue to evolve.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I learned about &lt;a href=&quot;https://jasonformat.com/islands-architecture&quot;&gt;&amp;quot;Islands Architecture&amp;quot;&lt;/a&gt; after joining &lt;a href=&quot;https://www.betterment.com/&quot;&gt;Betterment&lt;/a&gt; in 2019, where the implementation had been in production for at least 6 months as explorations into building richer, client-side experiences within the main Ruby on Rails app were starting to take hold. These &amp;quot;islands&amp;quot; were being built with &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; as the UI library of choice due to the mature tooling and community around it.&lt;/p&gt;
&lt;p&gt;The goal of this work was to allow developers to embed components into their templates with minimal effort and provide a positive user experiences for Betterment customers. To support a positive &lt;em&gt;developer&lt;/em&gt; experience while working on the React components, the code was isolated as an internal npm package within the same repo as the Rails application using &lt;a href=&quot;https://classic.yarnpkg.com/en/docs/workspaces&quot;&gt;Yarn v1 workspaces&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The package directory was bootstrapped with &lt;a href=&quot;https://create-react-app.dev/&quot;&gt;Create React App&lt;/a&gt; to reduce &lt;a href=&quot;https://en.wiktionary.org/wiki/bikeshedding&quot;&gt;bike-shedding&lt;/a&gt; the related bundling, linting, and testing tooling for the project. The separate workspace also allowed for setting up &lt;a href=&quot;https://storybook.js.org/&quot;&gt;Storybook&lt;/a&gt; for rapid visual feedback without running the entire Rails application. The Rails app could consume the internal package like any other from npm and be none-the-wiser to how it was built (for the most part). As this setup solidified, it was adopted by nearly every consumer-facing Rails app within the company.&lt;/p&gt;
&lt;h2&gt;Workflow&lt;/h2&gt;
&lt;p&gt;So how does it all work?&lt;/p&gt;
&lt;p&gt;Well, the core library for bridging the two worlds is &lt;a href=&quot;https://stimulus.hotwired.dev/&quot;&gt;StimulusJS&lt;/a&gt;. Betterment had already been using Stimulus for adding &amp;quot;sprinkles&amp;quot; of client-side behavior to server-rendered features for a while, so it made sense to adopt it as the mechanism for declaring &amp;quot;islands&amp;quot; within the Rails templates. Stimulus controllers are JS classes that are registered with a single &amp;quot;Application&amp;quot; object that instantiates those classes when it sees a matching &lt;code&gt;data-controller&lt;/code&gt; attribute in the DOM. The controllers have lifecycle methods for when it has been &amp;quot;connected&amp;quot;, or bound to the element with the &lt;code&gt;data-controller&lt;/code&gt; attribute, among others.&lt;/p&gt;
&lt;p&gt;Through this functionality, a &amp;quot;ReactAppController&amp;quot; was created to mount a React component to the bound element whenever it was declared in a Rails template; the element would also have a &lt;code&gt;data-react-app-name&lt;/code&gt; attribute to select the particular component for that island based on a map of registered components. Initial props could even be passed to the React component as a JSON string parsed by the controller before rendering the component.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// react-app-controller.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Controller &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;stimulus&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react-dom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; registry &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../registry&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt;&lt;/span&gt; Controller &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; App &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;App &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialProps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Could not resolve app with name: &#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;initial-props&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;We referred to these components as &amp;quot;apps&amp;quot; since &amp;quot;islands&amp;quot; was not as well-known at the time&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The Rails view usage:&lt;/p&gt;
&lt;pre class=&quot;language-erb&quot;&gt;&lt;code class=&quot;language-erb&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-controller&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;react-app&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-react-app-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;thing&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-react-app-initial-props&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground/tree/edd29d0eef8783148796784a4b39891929624e24&quot;&gt;View full example project based on this commit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To abstract over the implementation details of data attributes and make it feel more native to the application, a Rails UI component was created. We had an &lt;a href=&quot;https://www.betterment.com/engineering/design-components-rails&quot;&gt;internal gem for this purpose&lt;/a&gt;, but there is an open-source approximation that covers this need called &lt;a href=&quot;https://viewcomponent.org/&quot;&gt;ViewComponent&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactAppComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; ViewComponent&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;Base
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;initial_props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name
    &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initial_props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_json
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The associated template file for that component, which should look familiar:&lt;/p&gt;
&lt;pre class=&quot;language-erb&quot;&gt;&lt;code class=&quot;language-erb&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-controller&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;react-app&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-react-app-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; &lt;span class=&quot;token variable&quot;&gt;@name&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-react-app-initial-props&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Rails view usage:&lt;/p&gt;
&lt;pre class=&quot;language-erb&quot;&gt;&lt;code class=&quot;language-erb&quot;&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; render &lt;span class=&quot;token class-name&quot;&gt;ReactAppComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;thing&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;initial_props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground/tree/739041d6303780938adab96d311303e4585dd027&quot;&gt;View full example project based on this commit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With these pieces in place, teams could build new components, register them, and drop them into a Rails template without worrying about conflicting with another team&#39;s work.&lt;/p&gt;
&lt;h2&gt;In Practice&lt;/h2&gt;
&lt;p&gt;The ability to drop React components into Rails views stretched the definition of an &amp;quot;island&amp;quot; as teams used them in a variety of features, from interactive form elements for cropping images to complete page and routing takeovers. The system evolved slightly to handle the growing &amp;quot;registry&amp;quot; of components to ensure the size of the core JS bundle wasn&#39;t unmanageable by &lt;a href=&quot;https://reactjs.org/docs/code-splitting.html#reactlazy&quot;&gt;dynamically importing components and lazily-loading them&lt;/a&gt; within the Stimulus controller:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt;&lt;/span&gt; Controller &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; App &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Loading...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialProps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;element
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Could not resolve app with name: &#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;initial-props&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fallback behavior could eventually be configured from the Rails template so customers didn&#39;t just see &amp;quot;Loading...&amp;quot; while they waited for an experience to appear.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground/tree/aa0dfd6ca65fe9d9d2e5e3f08072ea8121947e2d&quot;&gt;View full example project based on this commit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Adding this lazy-loading behavior actually exposed a point of friction in the tooling setup; there wasn&#39;t a source of truth for sharing fixes or new functionality across the applications using the islands architecture, so each workspace had to be individually updated with this new behavior. The Stimulus controller relied on a reference to a local &amp;quot;registry&amp;quot; of components that was manually created for each workspace, which had to be registered with a central Stimulus Application. The code-splitting feature also relied on &lt;a href=&quot;https://guides.rubyonrails.org/webpacker.html&quot;&gt;Webpacker&lt;/a&gt; to process the dynamic import statements within the internal package source code to &amp;quot;chunk&amp;quot; the bundle correctly, which was a deprecated feature of the gem before the entire gem was eventually deprecated with the release of Rails 7. So if any existing or new app wanted to use islands, there was a tedious scaffolding process to go through first; usually through copy-pasting files across codebases or directories and hoping it worked.&lt;/p&gt;
&lt;p&gt;With the writing on the wall about the next generation of JS tooling and &lt;a href=&quot;https://guides.rubyonrails.org/working_with_javascript_in_rails.html&quot;&gt;Rails asset management&lt;/a&gt;, it&#39;s time to consider how &amp;quot;Islands on Rails&amp;quot; could evolve to solve the shortcomings listed above while taking advantage of new patterns.&lt;/p&gt;
&lt;h2&gt;Looking Forward&lt;/h2&gt;
&lt;p&gt;To summarize the core concerns to be addressed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;portability of JS controller to mount React islands&lt;/li&gt;
&lt;li&gt;manual setup and registration of islands in each workspace&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting with the portability problem, the web has had a solution for many years that works across all modern browsers: custom elements! If the earlier description of how Stimulus controllers work sounded familiar, then you&#39;ve probably built a custom element before. Custom elements don&#39;t need to contain styles or HTML; they can just be classes that encapsulate behavior and are associated with a unique tag in some HTML. Just like Stimulus controllers, they are registered with a central object known as the &lt;code&gt;CustomElementRegistry&lt;/code&gt; that is provided by the DOM instead of the application itself.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactIslandElement&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; App &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Suspense fallback&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loading...&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;App &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialProps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Suspense&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Could not resolve app with name: &#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialProps &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;react-island&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ReactIslandElement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Using Island here to lean into the concept a bit more&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This should nearly identical to the Stimulus implementation because they share a lot of similar properties, like the lifecycle method for when the class is bound to the DOM; it just needed a few changes for getting data attributes and updating &lt;code&gt;this.element&lt;/code&gt; to &lt;code&gt;this&lt;/code&gt; since the class itself &lt;em&gt;is a reference to the DOM element&lt;/em&gt;. No third-party dependencies required and using it in a Rails templates is even simpler:&lt;/p&gt;
&lt;pre class=&quot;language-erb&quot;&gt;&lt;code class=&quot;language-erb&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;react-island&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;thing&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-initial-props&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;react-island&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&#39;re working with a team that is familiar with Stimulus, there is a lightweight library that provides similar semantics through TypeScript decorators called &lt;a href=&quot;https://github.github.io/catalyst/guide/introduction/&quot;&gt;&lt;code&gt;@github/catalyst&lt;/code&gt;&lt;/a&gt;. It will also reduce some of the boilerplate for working with data attributes and defining the element:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; controller&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attr &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@github/catalyst&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;controller&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReactIslandElement&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attr&lt;/span&gt;&lt;/span&gt; props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; App &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Suspense fallback&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loading...&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;App &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialProps&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Suspense&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Could not resolve app with name: &#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initialProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the documented conventions of the catalyst project, the &lt;code&gt;ReactIslandElement&lt;/code&gt; will automatically be defined as &lt;code&gt;react-island&lt;/code&gt; through the &lt;code&gt;@controller&lt;/code&gt; decorator. Catalyst also comes with a useful feature I haven&#39;t seen in the Stimulus ecosystem yet: &lt;a href=&quot;https://hipsterbrown.com/training-data/islands-on-the-rails/&quot;&gt;lazily-defined controllers&lt;/a&gt;. Using the &lt;code&gt;lazyDefine&lt;/code&gt; function and a dynamic import statement, we start to get an experience similar to the &lt;a href=&quot;https://is-land.11ty.dev/&quot;&gt;&lt;code&gt;&amp;lt;is-land&amp;gt;&lt;/code&gt; element from 11ty&lt;/a&gt; where the loading can be delayed based on certain events:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; lazyDefine&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@github/catalyst&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;lazyDefine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;react-island&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./src/elements/react-island&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-erb&quot;&gt;&lt;code class=&quot;language-erb&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;react-island&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-load-on&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;visible&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;thing&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-initial-props&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token erb language-erb&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;&amp;lt;%=&lt;/span&gt;&lt;span class=&quot;token ruby language-ruby&quot;&gt; &lt;span class=&quot;token variable&quot;&gt;@initial_props&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;%&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;react-island&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the code for the island won&#39;t be loaded until the element is visible on the page! For any Rails app using the &lt;code&gt;ReactAppComponent&lt;/code&gt; ViewComponent, this is a single update to the component&#39;s template as an implementation detail.&lt;/p&gt;
&lt;p&gt;Now that there is a native way of providing the &amp;quot;island&amp;quot; behavior to the applications, how can it be packaged up into a reusable module for each workspace so island component registries don&#39;t need to be manually created for the custom element to use?&lt;/p&gt;
&lt;p&gt;Drawing inspiration from Stimulus once again, there are helper modules like &lt;a href=&quot;https://github.com/hotwired/stimulus-webpack-helpers&quot;&gt;&lt;code&gt;@hotwired/stimulus-webpack-helpers&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/ElMassimo/stimulus-vite-helpers&quot;&gt;&lt;code&gt;stimulus-vite-helpers&lt;/code&gt;&lt;/a&gt; that allow for dynamically loading and registering Stimulus controllers at build time by using specific syntax for &lt;a href=&quot;https://en.wikipedia.org/wiki/Glob_(programming)&quot;&gt;&amp;quot;globbing&amp;quot;&lt;/a&gt; files within a project. Looking at the Vite implementation more specifically, which extends the &lt;a href=&quot;https://vitejs.dev/guide/features.html#glob-import&quot;&gt;modern ESM import syntax&lt;/a&gt;, we could provide a module that takes in a &amp;quot;glob&amp;quot; of React island components to lazily import and provide as a registry to the &lt;code&gt;ReactIslandElement&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// registry.ts&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// fake package name used as an example&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; generateIslandRegistry &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react-islands&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; islands &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/**/islands/*.tsx&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/**/islands/*.jsx&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getIsland &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateIslandRegistry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;islands&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// verbosity used for clarity&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getIsland &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Renaming &lt;code&gt;getApp&lt;/code&gt; to &lt;code&gt;getIsland&lt;/code&gt; in this new implementation for the sake of consistency and clarity.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This module defines a convention of collecting all components within an &amp;quot;islands&amp;quot; directory in the workspace, similar to &lt;a href=&quot;https://fresh.deno.dev/&quot;&gt;Fresh&lt;/a&gt; from Deno. With this in place, any new islands added to the matching directory would be automatically registered and available to embed in the Rails app; however, this all still feels like boilerplate for each new workspace to scaffold. If we&#39;re already using a build tool to enable this feature, why not generate this boilerplate at build time?&lt;/p&gt;
&lt;p&gt;The list of &lt;a href=&quot;https://github.com/vitejs/awesome-vite#plugins&quot;&gt;community plugins for Vite&lt;/a&gt; provides some examples of extending the bundler to provide &lt;a href=&quot;https://vitejs.dev/guide/api-plugin.html#virtual-modules-convention&quot;&gt;virtual modules&lt;/a&gt; to a project. The plugin could generate the custom element and dynamic registry code as the single source of truth for React islands functionality. Each workspace would be set up in two files:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// vite.config.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; defineConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;vite&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; react &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@vitejs/plugin-react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; reactIslands &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;vite-plugin-react-islands&#39;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;defineConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reactIslands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// rest of the config specific to the workspace&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground/blob/ea1de03a2d20357d0c843abe4a670ad72af0438c/@playground/vite-plugin-react-islands/src/index.ts#L3&quot;&gt;plugin implementation source&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/index.ts (entrypoint for the project)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;virtual:react-islands&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;The &lt;code&gt;virtual:&lt;/code&gt; prefix is following Vite conventions for generated modules&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now any updates to the custom element or registry can be released through the plugin! If the project didn&#39;t want the separate JS workspace, the same setup could be applied to the Rails app through &lt;a href=&quot;https://vite-ruby.netlify.app/&quot;&gt;Vite Ruby&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;Overall, this feels like the right level of abstraction and quality-of-life improvement from the original setup while looking to the future of the web and JS tooling. You can find a &lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground&quot;&gt;complete example repo of this architecture on GitHub&lt;/a&gt; to try it out yourself. The Vite plugin is only an internal package to that repo at the moment; it could eventually be updated and published to support a variety of build tools through the &lt;a href=&quot;https://github.com/unjs/unplugin&quot;&gt;unified plugin system&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you learned something new, found this interesting, or have ideas about how it could be improved, please let me know on Twitter &lt;a href=&quot;https://twitter.com/hipsterbrown&quot;&gt;@hipsterbrown&lt;/a&gt;.  Cheers &amp;amp; happy building!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonus&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you made it this far and like the concept but want something a bit lighter than React for your islands, I have a branch of the repo to provide &lt;a href=&quot;https://github.com/HipsterBrown/rails-react-playground/tree/preact&quot;&gt;Preact in Rails&lt;/a&gt;; it can be compatible with the React implementation while being smaller in bundle size than the &lt;code&gt;react&lt;/code&gt; package by itself. There are trade-offs to consider that is probably a whole separate blog post to discover.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>My Self-Hosted Platform</title>
    <link href="https://hipsterbrown.com/training-data/my-self-hosted-platform/"/>
    <updated>Thu, 25 Aug 2022 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/my-self-hosted-platform/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;In light of the recent news about &lt;a href=&quot;https://blog.heroku.com/next-chapter#focus-on-mission-critical&quot;&gt;Heroku ending their free tier&lt;/a&gt;, I thought I would talk about the alternative platform I&#39;ve been using for several years (&lt;a href=&quot;https://dokku.com/&quot;&gt;Dokku&lt;/a&gt;) and why I think it is worth adopting.&lt;/p&gt;
&lt;h2&gt;What is Dokku?&lt;/h2&gt;
&lt;p&gt;A &amp;quot;Platform as a Service&amp;quot;, or PaaS, provides a way creating, managing, and deploying applications without the concerns of the underlying server infrastructure. Rather than needing to requisition a standalone or shared server, configure the application dependencies for the operating system, and set up some type of deployment pipeline for getting that code onto the server, i.e. &lt;a href=&quot;https://en.wikipedia.org/wiki/File_Transfer_Protocol&quot;&gt;FTP&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Rsync&quot;&gt;rsync&lt;/a&gt; for the old fashioned folks 😉, a PaaS abstracts over the details and make deployment as simple as &lt;code&gt;git push&lt;/code&gt;. They generally include additional integrations for things like databases, SSL/TLS certificates, and backups. This is the service provided by Heroku, among others, to enable more people to share their applications, from hobby projects to full businesses, and it looks like the former audience is being pushed away. Enter Dokku.&lt;/p&gt;
&lt;p&gt;From the documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dokku is an extensible, open source Platform as a Service that runs on a single server of your choice&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By using &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; and &lt;a href=&quot;https://github.com/gliderlabs/herokuish&quot;&gt;Heroku&#39;s open source Buildpack system&lt;/a&gt;, Dokku provides a service similar to Heroku that can be hosted on nearly any &lt;a href=&quot;https://dokku.com/docs/getting-started/advanced-installation/#configuring&quot;&gt;server hardware provider&lt;/a&gt; (including &lt;a href=&quot;https://dokku.github.io/release/dokku-0.27.x-wrapup#os-and-architecture-support&quot;&gt;Raspberry Pis!&lt;/a&gt;). Since I&#39;ve started using it, Dokku has added &lt;a href=&quot;https://dokku.com/docs/deployment/builders/builder-management/&quot;&gt;other builder options&lt;/a&gt; for deploying a variety of projects and grown its ecosystem of &lt;a href=&quot;https://dokku.com/docs/community/plugins/&quot;&gt;official and community plugins&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How I use Dokku&lt;/h2&gt;
&lt;p&gt;I currently host my Dokku instance on a $10 per month &amp;quot;droplet&amp;quot; on &lt;a href=&quot;https://dokku.com/docs/getting-started/install/digitalocean/&quot;&gt;DigitalOcean&lt;/a&gt;. This one droplet manages this static site (hipsterbrown.com), the private analytics service for this site (using &lt;a href=&quot;https://umami.is/docs/hosting&quot;&gt;Umami&lt;/a&gt;), and a &lt;a href=&quot;https://github.com/hipsterbrown/weekly-menu&quot;&gt;personal web app&lt;/a&gt;, along with the associated databases required by those services (PostgreSQL and CouchDB); all with auto-renewing &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;LetsEncrypt TLS certificates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My personal site is deployed automatically through a &lt;a href=&quot;https://github.com/HipsterBrown/hipsterbrown.com/blob/main/.github/workflows/deploy.yml&quot;&gt;GitHub Action&lt;/a&gt; that performs a &lt;code&gt;git push&lt;/code&gt; to Dokku. I perform manual pushes for the React app since it doesn&#39;t change as often. Finally, I use the Docker image maintained by the Umami team as the build target for that deployed application since it &amp;quot;just works.&amp;quot; This flexibility has allowed me to pick the best deployment method for each situation.&lt;/p&gt;
&lt;p&gt;While most folks will advise against hosting a database on the same server as the dependent application, none of that data is vital to me; so I am OK with the trade-off of convenience over durability.&lt;/p&gt;
&lt;h2&gt;Why I&#39;ll continue to use Dokku&lt;/h2&gt;
&lt;p&gt;Since I first set up Dokku, I have rarely needed to SSH into my server to manage the platform. Meanwhile, the project has many great updates with additional functionality and quality of life improvements that I could apply when I felt ready (made very approachable through the &lt;a href=&quot;https://dokku.com/docs/getting-started/upgrading/#upgrading-using-dokku-update&quot;&gt;&lt;code&gt;dokku-update&lt;/code&gt; command&lt;/a&gt;). Having the control over my personal platform has provided me the freedom to experiment without worrying about creating various different accounts or racking up unexpected bills due to forgotten projects. For a dollar less per month than the &amp;quot;Hobby&amp;quot; dyno on Heroku, it is possible to set up Dokku to host a Rail app and database with &lt;a href=&quot;https://dokku.com/docs/deployment/application-deployment/&quot;&gt;a similar expected workflow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I like to muck around with server management and &amp;quot;Dev Ops&amp;quot; infrastructure, so hosting Dokku has allowed me to do that while also letting me stay hands off for a while. If you never want to think about the server or SSH into an environment, Dokku might not be the right fit and there are plenty of other options these days, even for free. I&#39;m not claiming Dokku is the right or only way to deploy applications and services, rather this is my current preferred way for my needs. If this makes sense for you as well, &lt;a href=&quot;https://dokku.com/docs/getting-started/installation/&quot;&gt;give Dokku a try&lt;/a&gt; and feel free to let me know &lt;a href=&quot;https://twitter.com/hipsterbrown&quot;&gt;on Twitter&lt;/a&gt; how it goes.&lt;/p&gt;
&lt;p&gt;Cheers and happy hosting!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Components for Everyone</title>
    <link href="https://hipsterbrown.com/training-data/Components-for-Everyone/"/>
    <updated>Mon, 13 Sep 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Components-for-Everyone/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;&lt;em&gt;TL;DR This is not about web components / custom elements, rather why component-driven architecture for traditional server-rendered apps could be a better solution than adopting a client-side framework.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Recently, I&#39;ve started to form the opinion that client-side JS libraries, like Vue, React, Svelte, etc*, have grown in adoption within traditionally server-rendered applications due to component-driven view architecture more than any other feature they offer. I believe this core abstraction is key to why larger frameworks, like NextJS, Nuxt, Gatsby, and SvelteKit, are built atop those libraries with a targets of static site generation (SSG) or server-side rendering (SSR). This motivation can lead to further complexity around managing a distributed client-server codebase and constant context switching for full stack developers.&lt;/p&gt;
&lt;p&gt;For context, I&#39;ve been using React for ~6 years (starting as the view layer replacement for a &lt;a href=&quot;https://backbonejs.org/&quot;&gt;Backbone&lt;/a&gt; app) and currently work on a front end platform team that supports product squads using Rails and/or React to develop client-side features, depending on the experience of the team and requirements of the feature. Before adopting React a couple of years ago, &lt;a href=&quot;https://betterment.engineering/how-we-develop-design-components-in-rails-ab2d3dac44d3&quot;&gt;engineers at the company developed a Ruby component abstraction&lt;/a&gt; that allowed them to create a shared gem of design system components** for use across the production Rails applications. Even with React in active use, we still have this Ruby abstraction and are starting to adopt &lt;a href=&quot;https://viewcomponent.org/&quot;&gt;ViewComponent&lt;/a&gt; in its place to resolve issues around lack of documentation and surrounding tooling expected by devs who have used component-driven frameworks in the past, i.e. &lt;a href=&quot;https://storybook.js.org/&quot;&gt;Storybook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With that in mind, I wondered how many other teams chose to build features using a client-side JS framework primarily for built-in component model, consciously or not. The more I used a server-side component system, the more I recognized the things I liked about building React components, without the need to context switch between Ruby and JavaScript in a single feature.&lt;/p&gt;
&lt;p&gt;This led to some &lt;a href=&quot;https://www.urbandictionary.com/define.php?term=lazyweb&quot;&gt;lazy web&lt;/a&gt; research on Twitter to see if other folks thought about this subject; if so, where are they seeing this workflow in other frameworks?&lt;/p&gt;
&lt;p&gt;https://twitter.com/hipsterbrown/status/1433792423973605397?s=20&lt;/p&gt;
&lt;p&gt;While no one provided any other examples, outside of using SSR-only with one of the popular JS libraries I mentioned previously, there was enough engagement to continue exploring the wider web.&lt;/p&gt;
&lt;h2&gt;Workflow&lt;/h2&gt;
&lt;p&gt;Before diving into the various language implementations I found, I want to break down the core advantages the component model offers application devs. When I refer to &amp;quot;component-driven view architecture&amp;quot;, I am including the surrounding ecosystem of patterns and tooling as motivation for its accession:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;isolated development playground (Storybook / Pattern Lab)&lt;/li&gt;
&lt;li&gt;unit testing (quickly verify UI logic without spinning up a browser)&lt;/li&gt;
&lt;li&gt;packaging (ship as a reusable resource/gem/pub)&lt;/li&gt;
&lt;li&gt;sidecar assets (easily scope styles and behavior)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of those values can help scale the efficiency of teams building user interfaces, no matter the platform technology.&lt;/p&gt;
&lt;p&gt;Taking a look at open source design system components from large Rails shops like &lt;a href=&quot;https://primer.style/&quot;&gt;Primer&lt;/a&gt; from GitHub and &lt;a href=&quot;https://polaris.shopify.com/components/get-started&quot;&gt;Polaris&lt;/a&gt; from Shopify, the alternative to using JS components is usually collection of CSS classes; this can lead to inconsistent usage when applying them to markup and a worse developer experience. The &lt;a href=&quot;https://primer.style/view-components/&quot;&gt;Primer ViewComponents gem&lt;/a&gt; is still fairly new when compared to the CSS and React implementations of that design system but solves the job of providing reusable pieces of UI in a familiar interface to Rubyists, including an interactive &lt;a href=&quot;https://primer.style/view-components/stories/?path=/story/primer-button-group--button-group&quot;&gt;Storybook&lt;/a&gt; using &lt;a href=&quot;https://github.com/storybookjs/storybook/tree/next/app/server&quot;&gt;&lt;code&gt;@storybook/server&lt;/code&gt;&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Although I continue to mention the benefits in terms of pattern libraries, they can also be appreciated within a single app codebase to share common functionality across features.&lt;/p&gt;
&lt;h2&gt;Implementations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ruby / Rails&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have mentioned ViewComponent several times already, as the inspiration / motivation for this post. The docs do a great job explaining the philosophy behind the framework:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Traditional Rails templates have an implicit interface, making it hard to reason about their dependencies. This can lead to subtle bugs when rendering the same template in different contexts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://viewcomponent.org/#data-flow&quot;&gt;ViewComponent Data Flow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It has been growing in popularity since &lt;a href=&quot;https://github.blog/2020-12-15-encapsulating-ruby-on-rails-views/&quot;&gt;its introduction by GitHub&lt;/a&gt;; looking at the open source projects using ViewComponent: &lt;a href=&quot;https://opensourcerails.org/search/by-gem/view_component&quot;&gt;https://opensourcerails.org/search/by-gem/view_component&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But that&#39;s not the only solution on the block, especially when looking to build a static site. &lt;a href=&quot;https://www.bridgetownrb.com/&quot;&gt;Bridgetown&lt;/a&gt; bills itself as &amp;quot;a fast, scalable, modular, and thoroughly forward-looking framework for building websites and frontend applications&amp;quot; and a modern successor to &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;. Although it also &lt;a href=&quot;https://www.bridgetownrb.com/release/embracing-ruby-in-0.21/&quot;&gt;supports ViewComponent&lt;/a&gt;, the Bridgetown team is the first place I&#39;ve seen an implementation of &lt;a href=&quot;https://www.bridgetownrb.com/docs/components/liquid&quot;&gt;components for the Liquid templating language&lt;/a&gt;. &lt;a href=&quot;https://github.com/bridgetownrb/liquid-component&quot;&gt;Liquid component&lt;/a&gt; does not go as far as ViewComponent in providing a Ruby class as the interface for a component, but uses YAML front matter well to document and describe pieces of reusable view logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP / Laravel&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It appears there has been a concept of &amp;quot;components&amp;quot; in Laravel&#39;s Blade templates system since &lt;a href=&quot;https://laravel.com/docs/5.4/blade#components-and-slots&quot;&gt;version 5.4&lt;/a&gt;; however they are more comparable to partials with support for slots until &lt;a href=&quot;https://laravel.com/docs/7.x/blade#components&quot;&gt;version 7&lt;/a&gt;. This current iteration of Blade component is backed by a PHP class with an associated template (or an &lt;a href=&quot;https://laravel.com/docs/8.x/blade#inline-component-views&quot;&gt;inline component view&lt;/a&gt;), which is more similar to ViewComponent. These components are rendered using a familiar kebab-case, HTML tag-like syntax: &lt;code&gt;&amp;lt;x-alert&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python / Django&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.com/Mojeer/django_components&quot;&gt;&lt;code&gt;django-component&lt;/code&gt;&lt;/a&gt; provides &amp;quot;declarative and composable components inspired by javascript frameworks&amp;quot;. They are made up of a Python class and associated HTML template, like the previously mentioned ViewComponent and Blade components.&lt;/p&gt;
&lt;p&gt;There is also a library called &lt;a href=&quot;https://mitchel.me/slippers/&quot;&gt;&lt;code&gt;slippers&lt;/code&gt;&lt;/a&gt;, that reminds me of liquid components more than ViewComponent since they don&#39;t rely on some kind of class or backing data structure defined outside the template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;.Net / Blazor&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As a modern replacement for Web Forms, &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/architecture/blazor-for-web-forms-developers/components#use-components&quot;&gt;Blazor supports UI encapsulation through components&lt;/a&gt; using the &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0&quot;&gt;Razor templating language&lt;/a&gt; to define markup and logic in a single file; although that logic can be &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/architecture/blazor-for-web-forms-developers/components#code-behind&quot;&gt;broken out as a separate C# class&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Elixir / Phoenix&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#content&quot;&gt;&lt;code&gt;Phoenix.Component&lt;/code&gt; API&lt;/a&gt; allows Elixir developers to create function components, similar to &lt;a href=&quot;https://reactjs.org/docs/components-and-props.html#function-and-class-components&quot;&gt;React&lt;/a&gt;, including ability to pass in &amp;quot;assigns&amp;quot; (props) and &amp;quot;blocks&amp;quot; (children).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://surface-ui.org/documentation&quot;&gt;Surface UI&lt;/a&gt; builds on top of the Phoenix component API to provide &amp;quot;a fast and productive solution to build modern web applications&amp;quot;. It uses an extended templating syntax, helpful macros, and built-in components to become a more full-featured view framework for Phoenix applications.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JS / Node&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Lastly, we get to the curious case of server-rendered component systems for NodeJS. The closest library in comparison to the previous examples is &lt;a href=&quot;https://markojs.com/&quot;&gt;Marko&lt;/a&gt;, which allows developers to &amp;quot;start with simple HTML templates and add powerful components as needed&amp;quot;. Marko templating can be integrated into &lt;a href=&quot;https://markojs.com/docs/server-integrations-overview/&quot;&gt;popular server libraries&lt;/a&gt; as the core view engine, rather than &lt;a href=&quot;https://reactjs.org/docs/react-dom-server.html&quot;&gt;forcing a typical client-side framework into that workflow&lt;/a&gt;. There was an official view engine for &lt;a href=&quot;https://github.com/reactjs/express-react-views&quot;&gt;React in Express&lt;/a&gt;, but it no longer appears actively supported; &lt;a href=&quot;https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html&quot;&gt;React Server Components&lt;/a&gt; are also still theoretical at the time of writing.&lt;/p&gt;
&lt;p&gt;There is active development in this space with the advent of tools like &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; and &lt;a href=&quot;https://slinkity.dev/&quot;&gt;Slinkity&lt;/a&gt; that aim to take a framework-agnostic approach to introduce components into modern, HTML-first static sites. Astro does a great job &lt;a href=&quot;https://docs.astro.build/comparing-astro-vs-other-tools&quot;&gt;comparing itself to existing tools&lt;/a&gt; and Slinkity is building on the shoulders of the awesome &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; ecosystem (slight bias given this site is currently built with Eleventy).&lt;/p&gt;
&lt;p&gt;Previously, it seemed like NodeJS devs didn&#39;t invest in a server-side component framework because existing client-side libraries could be worked into the SSR pipeline; why reinvent the wheel? There is less of a context switch in terms of syntax when compared to the workflows of other web languages, so the friction lies in the &lt;a href=&quot;https://crystallize.com/blog/frontend-performance-react-ssr-and-the-uncanny-valley&quot;&gt;uncanny valley&lt;/a&gt; of managing a client-side component system in a traditional server application. Even if a library like React or Vue is used as SSR-only, the APIs were designed for the front end, which can lead to confusion about how to constrain the usage of third-party libraries and patterns.&lt;/p&gt;
&lt;p&gt;Who knows where the future is headed in this use case? Could there be some type of &lt;a href=&quot;https://github.com/janl/mustache.js/&quot;&gt;mustache.js&lt;/a&gt; extension or &lt;a href=&quot;https://handlebarsjs.com/&quot;&gt;Handlebars&lt;/a&gt; helper for rendering a JavaScript class in a template? That could be pretty neat!&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This post is not meant to denigrate client-side component frameworks; I love developing front end applications with JavaScript and will continue to do so. But choosing to build with that stack comes with additional complexity that not every team is ready to handle, so there should be useful alternatives for server-rendered ecosystems and take advantage of that environment. I hope we see more tools like &lt;a href=&quot;https://www.npmjs.com/package/@storybook/server&quot;&gt;Storybook Server&lt;/a&gt; being used to develop those server components in isolation and generate documentation for easier discoverability. There should be a rich collection of accessible, open source component libraries, similar to &lt;a href=&quot;https://headlessui.dev/&quot;&gt;Headless UI&lt;/a&gt; or &lt;a href=&quot;https://www.radix-ui.com/docs/primitives/overview/introduction&quot;&gt;Radix UI&lt;/a&gt;, so apps can ship features that are enjoyed by everyone by default.&lt;/p&gt;
&lt;p&gt;If I missed something, like an example implementation or assumed detail, please reach out on &lt;a href=&quot;https://twitter.com/hipsterbronw&quot;&gt;Twitter&lt;/a&gt; to help me correct it here. I&#39;ll continue to shape these thoughts, put them into practice through personal and professional projects, and follow up with more articles to share any lessons learned.&lt;/p&gt;
&lt;p&gt;* I purposely left out more complete frameworks like Ember and Angular since they focus on shipping full client-side applications out of the box.&lt;/p&gt;
&lt;p&gt;** This was created before my time at the company but is explained well in this Rails Conf talk from 2018: &lt;a href=&quot;https://www.youtube.com/watch?v=Egumr5KiTNI&quot;&gt;https://www.youtube.com/watch?v=Egumr5KiTNI&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to read TypeScript errors</title>
    <link href="https://hipsterbrown.com/training-data/read-typescript-errors/"/>
    <updated>Tue, 08 Jun 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/read-typescript-errors/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;At some point when writing code, everyone runs into errors. These errors can occur at runtime, build time, or as feedback in editors while the code is being written. When seeing TypeScript errors for the first time, they can range from helpful to WTF.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Type &lt;span class=&quot;token string&quot;&gt;&#39;{ children: string; onClick: (event: MouseEvent&amp;lt;HTMLAnchorElement, MouseEvent&gt;) =&gt; void; fontSize: string; pb: string; mr: string; to: string; exact: true; activeClassName: string; }&#39;&lt;/span&gt; is not assignable to &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IntrinsicAttributes &amp;amp; NavLinkProps&amp;lt;unknown&gt; &amp;amp; RefAttributes&amp;lt;NavLink&amp;lt;unknown&gt;&gt; &amp;amp; { theme?: StyleClosetTheme | undefined; } &amp;amp; { ...; } &amp;amp; Pick&amp;lt;...&gt; &amp;amp; { ...; }&#39;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
  Property &lt;span class=&quot;token string&quot;&gt;&#39;fontSize&#39;&lt;/span&gt; does not exist on &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;IntrinsicAttributes &amp;amp; NavLinkProps&amp;lt;unknown&gt; &amp;amp; RefAttributes&amp;lt;NavLink&amp;lt;unknown&gt;&gt; &amp;amp; { theme?: StyleClosetTheme | undefined; } &amp;amp; { ...; } &amp;amp; Pick&amp;lt;...&gt; &amp;amp; { ...; }&#39;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;  TS2322

     &lt;span class=&quot;token number&quot;&gt;97&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ActiveLink
  &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;98&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;token assign-left variable&quot;&gt;fontSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     ^
     &lt;span class=&quot;token number&quot;&gt;99&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;token assign-left variable&quot;&gt;pb&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;4&quot;&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;token assign-left variable&quot;&gt;mr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;9&quot;&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;101&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;token assign-left variable&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above error is only a couple lines but has verbose and truncated type output.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get it in realtime.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Over the last few years, I&#39;ve grown to appreciate the in-editor feedback available to many languages, including TypeScript. This feature is either built-in or available as a plugin to all sorts of dev environments, from vim to Visual Studio Code, which are my main choices. Even ignoring the nice refactoring tools, live feedback is incredibly useful for associating errors with the exact lines in the code with as much context as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Starting from the bottom.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While some compilers can provide pin point feedback and helpful suggestions for fixes, the TypeScript compiler errors still have a way to go. When viewing these verbose error traces, it&#39;s been helpful to view the bottom message and work my way up. These last messages usually provide the most specific comparison between mismatched types or inference issues.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where did that come from?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Speaking of inference, one of the handy and hurtful things about TypeScript is the ability to &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html#types-by-inference&quot;&gt;assign types by inference&lt;/a&gt;, so not everything needs an explicit type declaration. The compiler tries its best to get this right, but when it doesn&#39;t, it can be a rabbit hole of figuring out where the inferred type came from. This leads to another push for enabling in-editor feedback to help trace assumed types back to the source. Adding explicit declarations during this exploration can help narrow the issue, even if it feels like TS should just know the correct type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&#39;t be afraid to dig.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A few of the worst offenders in previous debug sessions have come from clashing types and interfaces between third-party libraries and my own code. But this has also led to a better understanding when reading other people&#39;s typed code. When looking into the source of a compiler error, feel free to jump to the definition of the flagged type and follow the thread of code to its eventual conclusion if needed. Ideally, the authors will have some comments (maybe even &lt;a href=&quot;http://typedoc.org/&quot;&gt;typedoc&lt;/a&gt; and clearly named generic types; however, there can be a lot of &amp;quot;clever&amp;quot; code out there to make a dynamic API work with the compiler.&lt;/p&gt;
&lt;p&gt;I hope these tips are helpful the next time the red squiggle of doom appears in your editor and displays an intimidating stack of feedback.&lt;/p&gt;
&lt;p&gt;Other resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/innovation-and-technology/deciphering-typescripts-react-errors-8704cc9ef402&quot;&gt;Deciphering TypeScript&#39;s React errors&lt;/a&gt;: goes in-depth into specific errors seen in a React codebase.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/Docs/languages/typescript&quot;&gt;TypeScript in Visual Studio Code&lt;/a&gt;: shows how to use the TS feedback and features in the popular editor.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/neoclide/coc.nvim#quick-start&quot;&gt;coc.nvim quick start&lt;/a&gt;: provides set up instructions for using this vim plugin with TypeScript support out of the box.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Typed mocks for Jest</title>
    <link href="https://hipsterbrown.com/training-data/typed-jest-mocks/"/>
    <updated>Tue, 04 May 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/typed-jest-mocks/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;&lt;em&gt;TL:DR &lt;a href=&quot;https://www.npmjs.com/package/jest-when&quot;&gt;&lt;code&gt;jest-when&lt;/code&gt;&lt;/a&gt; and its &lt;a href=&quot;https://www.npmjs.com/package/@types/jest-when&quot;&gt;associated types package&lt;/a&gt; are useful additions to any TypeScript codebase looking for a nicer way to use Jest mocks.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;ve ever worked on a TypeScript project that used &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt; as its testing framework, you&#39;ve most likely run into the issue of using a mocked function, method, or class in your test setup and seen a compiler error like &amp;quot;Property &#39;mockImplementation&#39; does not exist on type &#39;blah&#39;.&amp;quot; Even though you know you&#39;ve replaced that imported dependency, TypeScript is still using the regular types for it. When looking at the docs, the &lt;a href=&quot;https://jestjs.io/docs/mock-function-api#jestmockedfunction&quot;&gt;&lt;code&gt;jest.MockedFunction&lt;/code&gt; type&lt;/a&gt; is recommended as a solution for resolving this issue:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; myFunction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mockMyFunction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myFunction &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MockedFunction&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; myFunction&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockMyFunction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;calls&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be quite tedious when done several times in the setup for a test, so my coworkers and I came up with a helper called &lt;code&gt;mockOfFunction&lt;/code&gt; that made this process a bit cleaner:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in some shared testing helpers file&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; mockOfFunction&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Mock&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ReturnType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Mock&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ReturnType&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// in a test setup&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; myFunction &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; _myFunction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; mockOfFunction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./testing-helpers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myFunction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mockOfFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_myFunction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this quieted the compiler&#39;s complaints, the abstraction caused some confusion among team members who were newer to TypeScript and Jest; leading them to believe that &lt;code&gt;mockOfFunction&lt;/code&gt; was doing the actual mocking rather than &lt;code&gt;jest.mock&lt;/code&gt;. In an effort to help improve my team&#39;s testing workflow, I sought out a better solution and discovered &lt;a href=&quot;https://github.com/timkindberg/jest-when&quot;&gt;&lt;code&gt;jest-when&lt;/code&gt;&lt;/a&gt; in the process.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;jest-when&lt;/code&gt; allows you to use a set of the original Jest mock functions in order to train your mocks only based on parameters your mocked function is called with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Given the API provided by &lt;code&gt;jest-when&lt;/code&gt;, any imported mocked dependency could use the allowed &lt;a href=&quot;https://jestjs.io/docs/mock-function-api#reference&quot;&gt;mocking methods&lt;/a&gt; without worrying about &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions&quot;&gt;type assertions&lt;/a&gt; and odd setup boilerplate.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; when &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;jest-when&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; myFunction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
jest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./library&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myFunction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mockReturnValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/timkindberg/jest-when#introduction&quot;&gt;https://github.com/timkindberg/jest-when&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The actual purpose of providing more robust and specific mocking behavior to Jest was an added bonus in my eyes, especially as it looked familiar to &lt;a href=&quot;https://www.npmjs.com/package/testdouble#tdwhen-for-stubbing-responses&quot;&gt;the &lt;code&gt;when&lt;/code&gt; method from &lt;code&gt;testdouble&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using this library has been particularly helpful when stubbing feature flag responses:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token function&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;useFeatureFlag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calledWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-feature-flag&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mockReturnValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; enabled&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whereas previously, any feature flag request would have been stubbed with the same response and making it nearly impossible to distinguish expected output related to overlapping flags or experiments.&lt;/p&gt;
&lt;p&gt;So I highly recommend checking out &lt;a href=&quot;https://github.com/timkindberg/jest-when&quot;&gt;&lt;code&gt;jest-when&lt;/code&gt;&lt;/a&gt; and improving your mocking setup, especially when dealing with TypeScript.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Shipping Web Workers Simply</title>
    <link href="https://hipsterbrown.com/training-data/tagged-template-web-worker/"/>
    <updated>Sun, 02 May 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/tagged-template-web-worker/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;I was planning a feature that needed some kind of background process on the front-end, which led me to exploring &lt;a href=&quot;http://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API&quot;&gt;Web Workers&lt;/a&gt; in a production use case for the first time. As part of building this feature, I wanted to be able to share the Worker through a common library that each app depended on. This requirement is not addressed by most articles that talk about Workers; usually showing examples of code defined in a separate file that is referenced by that file&#39;s name when creating a Worker instance.&lt;/p&gt;
&lt;p&gt;Based on the &lt;a href=&quot;https://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers&quot;&gt;inline web worker example from HTML5 Rocks&lt;/a&gt;, I created a &lt;a href=&quot;http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates&quot;&gt;tagged template helper&lt;/a&gt; to generate a Worker instance:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;strings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;interpolatedValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_zip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interpolatedValues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/javascript&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or without the &lt;code&gt;lodash&lt;/code&gt; dependency:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;strings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;interpolatedValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;strings&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interpolatedValues&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; current&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length
          &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/javascript&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could be made even simpler by not allowing interpolated values to be used in the script, but, by allowing this usage, data can be passed to the Worker definition when it is created! Since I was using TypeScript for this project, I could define a typed and tested function that could be colocated with the feature that consumes and interacts with the inlined worker.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Pick&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;postMessage&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;eventHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MyCustomEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-event&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// perform some background work&lt;/span&gt;
      self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-response&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; someData &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;startWorker&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; myWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; worker&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
    self.onmessage = &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;createHandler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;(self);
  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  myWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-response&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// react to the worker response&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  myWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-event&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a fun problem to solve and has worked out well so far as the feature has been running in production for some time. I didn&#39;t create a package or example repo for this helper since it is simple enough to copy and paste into whichever project needs it without me dedicating some amount of time to maintain the code.&lt;/p&gt;
&lt;p&gt;Feel free to share some use case examples with me &lt;a href=&quot;https://twitter.com/hipsterbrown&quot;&gt;on Twitter&lt;/a&gt;. I&#39;d love to see them!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Decorate Storybook with MirageJS</title>
    <link href="https://hipsterbrown.com/training-data/storybook-mirage-addon/"/>
    <updated>Tue, 13 Apr 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/storybook-mirage-addon/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;&lt;em&gt;TL;DR Check out the published addon: &lt;a href=&quot;https://www.npmjs.com/package/storybook-mirage&quot;&gt;&lt;code&gt;storybook-mirage&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When building and testing an application or service, eventually the need to mock communication with another service arises. For client-side web applications, that usually means mocking HTTP requests to a server. Starting out, this could be done using a library like &lt;a href=&quot;https://github.com/wheresrhys/fetch-mock&quot;&gt;fetch-mock&lt;/a&gt; or simply manually mocking methods on an API client module using &lt;a href=&quot;https://jestjs.io/docs/mock-functions#mocking-modules&quot;&gt;&lt;code&gt;jest.mock&lt;/code&gt;&lt;/a&gt;. As the app grows and/or the team expands, something more robust and realistic is required to scale, as well as provide the ability to mock requests outside of the test environment; a downside of the solutions mentioned previously.&lt;/p&gt;
&lt;p&gt;This is where tooling like &lt;a href=&quot;https://miragejs.com/&quot;&gt;MirageJS&lt;/a&gt; comes in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mirage JS is an API mocking library that lets you build, test and share a complete working JavaScript application without having to rely on any backend services.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://miragejs.com/&quot;&gt;https://miragejs.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By intercepting outgoing HTTP requests, Mirage can exist outside a test environment and provide a source of truth for interacting with an external service. The &lt;a href=&quot;https://miragejs.com/docs/getting-started/introduction/&quot;&gt;docs&lt;/a&gt; share the many scenarios and setups in which Mirage can be useful when developing an app. I appreciate the &lt;a href=&quot;https://miragejs.com/docs/main-concepts/route-handlers/&quot;&gt;familiar route handler syntax&lt;/a&gt; and use of &lt;a href=&quot;https://miragejs.com/docs/main-concepts/factories/&quot;&gt;factories&lt;/a&gt; for quick data creation without leaking too much of the implementation. In my experience so far, Mirage has allowed several teams to collaborate with this shared server interface; speeding up their testing and prototyping workflow. In regards to prototyping, the Mirage docs do not yet specify it can be used with &lt;a href=&quot;https://storybook.js.org/&quot;&gt;Storybook&lt;/a&gt;, which is what this article hopes to provide.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Storybook is an open source tool for developing UI components in isolation for React, Vue, Angular, and more. It makes building stunning UIs organized and efficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://storybook.js.org/&quot;&gt;https://storybook.js.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Storybook provides &lt;a href=&quot;https://storybook.js.org/docs/react/writing-stories/decorators#gatsby-focus-wrapper&quot;&gt;“decorators”&lt;/a&gt; as a way to “augment your stories with extra rendering or gather details about how your story is rendered”.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If your components are “connected” and require side-loaded data to render, you can use decorators to provide that data in a mocked way, without having to refactor your components to take that data as an arg.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://storybook.js.org/docs/react/writing-stories/decorators#using-decorators-to-provide-data&quot;&gt;&lt;em&gt;Using decorators to provide data&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By defining a functional component that starts and loads a Mirage server, the stories can be decorated with mocked API handlers.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// withServer.js&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  useEffect&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  useRef&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@storybook/addons&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;withServer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;makeServer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;StoryFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shutdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;StoryFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The decorator uses hooks from the &lt;a href=&quot;https://www.npmjs.com/package/@storybook/addons&quot;&gt;&lt;code&gt;@storybook/addons&lt;/code&gt;&lt;/a&gt; package. It is set up with the &lt;a href=&quot;https://miragejs.com/docs/workflow-tips/#centralize-and-share-your-server-between-development-and-testing&quot;&gt;server creator function&lt;/a&gt;, which is cleaned up when the story is unmounted.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .storybook/preview.js&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; decorators &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;makeServer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the example decorator simply starts and stops the Mirage server, it could be configured for each story or component through the &lt;code&gt;mirage&lt;/code&gt; parameter:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// withServer.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;withServer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;makeServer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;StoryFn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; logging&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fixtures&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handlers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timing&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; instance &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useParameter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;mirage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;handlers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;fixtures&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;timing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// rest of setup in useEffect&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Page.stories.js | Page.stories.ts&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Page &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./Page&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Page&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;mirage&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// automatically log requests to browser console https://miragejs.com/api/classes/server/#logging&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// customize when a request responds https://miragejs.com/docs/main-concepts/route-handlers/#timing&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;timing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// override route handlers for the story https://miragejs.com/docs/main-concepts/route-handlers/&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;handlers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token string-property property&quot;&gt;&#39;/api/user&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// status code&lt;/span&gt;
          &lt;span class=&quot;token string-property property&quot;&gt;&#39;/api/items&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;204&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// arguments for Response https://miragejs.com/api/classes/response/&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token string-property property&quot;&gt;&#39;api/task&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// body for Response&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// data to seed Mirage ORM https://miragejs.com/docs/main-concepts/fixtures/&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;fixtures&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// pass in a custom Mirage server instance to override the global setting&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All this will allow components to make requests like normal, captured by Mirage with the same behavior as when using it in normal development and testing.&lt;/p&gt;
&lt;p&gt;By &lt;a href=&quot;https://storybook.js.org/tutorials/create-an-addon/react/en/track-state/&quot;&gt;tracking state in the decorator&lt;/a&gt;, the integration could become more interactive when displayed in a custom &lt;a href=&quot;https://storybook.js.org/docs/react/addons/addon-types#panels&quot;&gt;panel&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/HipsterBrown/storybook-mirage/raw/badf9194699ca0e3fcaf319b3eb63668b267c2d3/screenshots/custom-response-panel.png&quot; alt=&quot;Custom storybook panel to modify response data and status&quot; /&gt;
&lt;em&gt;Custom storybook panel to modify the response data and status&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/HipsterBrown/storybook-mirage/raw/badf9194699ca0e3fcaf319b3eb63668b267c2d3/screenshots/request-logs-panel.png&quot; alt=&quot;Custom storybook panel to view request logs&quot; /&gt;
&lt;em&gt;View request logs handled by Mirage&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Combined with the ability to &lt;a href=&quot;https://storybook.js.org/docs/react/workflows/unit-testing#gatsby-focus-wrapper&quot;&gt;reuse stories in unit test scenarios&lt;/a&gt;, building features that depend on remote data sources has become incredibly streamlined and independent of running the complete app locally. I hope you find this knowledge and workflow as productive as I have.&lt;/p&gt;
&lt;p&gt;The addon is published here: &lt;a href=&quot;https://www.npmjs.com/package/storybook-mirage&quot;&gt;&lt;code&gt;storybook-mirage&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Using esbuild with 11ty</title>
    <link href="https://hipsterbrown.com/training-data/esbuild-with-11ty/"/>
    <updated>Tue, 30 Mar 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/esbuild-with-11ty/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;A little while ago, I migrated my personal site to &lt;a href=&quot;https://11ty.dev/&quot;&gt;11ty&lt;/a&gt; as the static site engine; moving away from &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; for various reasons: speed, language familiarity, and community. Given 11ty&#39;s sole focus on being a great static site builder, there isn&#39;t a built-in concern for processing or bundling other assets like CSS and JavaScript. While using Jekyll, I had &lt;a href=&quot;https://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; set up to process &lt;a href=&quot;https://sass-lang.com/&quot;&gt;Sass&lt;/a&gt;, minify JS, and create an SVG icon map. Since I replaced the SVG icon map with inline &lt;code&gt;include&lt;/code&gt; expressions, I wanted a different build system to process the Sass and JS that could integrate cleanly with 11ty.&lt;/p&gt;
&lt;p&gt;During my initial research, I looked into the &lt;a href=&quot;https://www.11ty.dev/docs/starter/&quot;&gt;starter projects&lt;/a&gt; shared through the 11ty docs. There&#39;s a wide range of options, from simple résumé sites to full e-commerce solutions and remote CMS sources. Much like the story of the three bears, nothing seemed to fit quite right because they either did more than required or not enough. Most of the solutions also required running a one or more separate processes outside of 11ty. This led me to assembling my own pipeline using &lt;a href=&quot;https://www.11ty.dev/docs/plugins/&quot;&gt;11ty plugins&lt;/a&gt;, which is another plethora of choice for most things except processing JS and Sass. The few plugins for handling styles appeared to &lt;a href=&quot;https://en.wikipedia.org/wiki/Monkey_patch&quot;&gt;monkeypatch&lt;/a&gt; the Eleventy class, which felt fragile to depend on.&lt;/p&gt;
&lt;p&gt;Finally, I got around to looking at &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;&lt;code&gt;esbuild&lt;/code&gt;&lt;/a&gt; to solve my problem. I&#39;ve used it at work to improve the performance of TypeScript and React bundling, although the scale of my site is considerably smaller. esbuild doesn&#39;t handle Sass out of the box (&lt;a href=&quot;https://esbuild.github.io/content-types/#css&quot;&gt;CSS support is in progress at the time of writing&lt;/a&gt;), but it too has &lt;a href=&quot;https://github.com/esbuild/community-plugins&quot;&gt;a plugin system&lt;/a&gt; to help extend its capabilities. I landed on the &lt;a href=&quot;https://www.npmjs.com/package/esbuild-sass-plugin&quot;&gt;&lt;code&gt;esbuild-sass-plugin&lt;/code&gt;&lt;/a&gt; because it looked up to date, performant, and provided the choice between &lt;a href=&quot;https://github.com/sass/node-sass&quot;&gt;node-sass&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/package/sass&quot;&gt;dart sass&lt;/a&gt;. Integrating was just a matter of calling esbuild in &lt;a href=&quot;https://www.11ty.dev/docs/events/#afterbuild&quot;&gt;11ty&#39;s &amp;quot;afterBuild&amp;quot; event handler&lt;/a&gt; to ensure the output directory was created by 11ty before processing the Sass and JS.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .eleventy.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; esbuild &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;esbuild&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; sassPlugin &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;esbuild-sass-plugin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;afterBuild&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; esbuild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;entryPoints&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sass/app.scss&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;js/app.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;outdir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_site/assets&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;minify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEVENTY_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;production&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;sourcemap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEVENTY_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;production&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sassPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addWatchTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./sass/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addWatchTarget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./js/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// continue configuring 11ty&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.11ty.dev/docs/watch-serve/#add-your-own-watch-targets&quot;&gt;&lt;code&gt;addWatchTarget&lt;/code&gt; method&lt;/a&gt; helps trigger the esbuild when one of the entrypoints, or files related to those entrypoints, has changed. Keeping 11ty in charge of file watching maintains a source of truth for build triggers, rather than having competing watchers when using separate processes. Even though esbuild does support &lt;a href=&quot;https://esbuild.github.io/api/#incremental&quot;&gt;incremental builds&lt;/a&gt;, the amount of files being processed is so small that it would be overkill to use this feature and means I&#39;m not concerned about the uneccessary builds that happen while I&#39;m not working on those assets. A nice perk from this overall setup meant converting my JavaScript to TypeScript just by renaming my source file and esbuild did the rest.&lt;/p&gt;
&lt;p&gt;All in all, its nice to know that extending 11ty is supported by a rich community of examples, plugins, and documentation. If anything needs to change in the future, I&#39;m confident I could find a way forward.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;P.S.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;During discovery for this post, I found &lt;a href=&quot;https://github.com/jamshop/eleventy-plugin-esbuild&quot;&gt;eleventy-plugin-esbuild by jamshop&lt;/a&gt; as a pre-built solution for what I ended up doing myself. The core differences with this implementation is the use of the synchronous version of esbuild&#39;s processing and manages its own build cache as &lt;a href=&quot;https://www.11ty.dev/docs/data-global-custom/&quot;&gt;custom global data&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Wrangle complexity with finite-state reducers</title>
    <link href="https://hipsterbrown.com/training-data/wrangle-complexity-with-finite-state-reducer/"/>
    <updated>Tue, 23 Mar 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/wrangle-complexity-with-finite-state-reducer/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;&lt;em&gt;While the examples include references to React, the ideas are applicable to any UI development systems.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;ve built interfaces before, you&#39;ve probably run into this issue: &lt;strong&gt;state explosion&lt;/strong&gt;. What starts out with one or two pieces of data that affect the output of your component, widget, etc, over time grows into several intertwined variables and flags that needs to be maintained by other team members (or your future self at the very least).&lt;/p&gt;
&lt;p&gt;Let&#39;s take a look at building a basic accordion component.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An accordion is a vertically stacked set of interactive headings that each contain a title, content snippet, or thumbnail representing a section of content. The headings function as controls that enable users to reveal or hide their associated sections of content. Accordions are commonly used to reduce the need to scroll when presenting multiple sections of content on a single page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/wai-aria-practices-1.2/#accordion&quot;&gt;WAI-ARIA Authoring Practices&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Based on the specification, we&#39;ll need to control which panel is being displayed while closing any open panels. To keep the scope of work fairly small, we&#39;re only going to manage two panels and use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details&quot;&gt;&lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;&lt;/a&gt; element to control showing the content.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyAccordion&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onFirstPanelClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setSecondPanelOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setFirstPanelOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentState &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;currentState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSecondPanelClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setFirstPanelOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setSecondPanelOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentState &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;currentState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;details&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;firstPanel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onFirstPanelClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;summary&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-level&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;tabIndex&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;aria-expanded&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;aria-controls&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;firstPanel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isFirstPanelOpen &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Close&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; My First Panel
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Some basic content goes here&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;details&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;details&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;secondPanel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onSecondPanelClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;summary&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-level&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;tabIndex&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;aria-expanded&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;aria-controls&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;secondPanel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isSecondPanelOpen &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Close&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; My Second Panel
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Some more basic content goes here&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;details&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View live example on CodePen: &lt;a href=&quot;https://codepen.io/HipsterBrown/details/qBRWbOG&quot;&gt;https://codepen.io/HipsterBrown/details/qBRWbOG&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The state management code appears simple enough with two boolean state variables and two click handlers. The state variables are toggling the &lt;code&gt;open&lt;/code&gt; states of the two &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; elements, as well as the &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; text for each panel. Each time we toggle one piece of state, we may end up toggling another since only one panel should be open at a time. Adding more panels would quickly lead to the state explosion introduced in the beginning of this post. Even in this pared-down version, it would be tough to guarantee our feature is working at a glance.&lt;/p&gt;
&lt;p&gt;At this point, it might be a good idea to reach for a reducer to help with this problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usereducer&quot;&gt;React docs &lt;code&gt;useReducer&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let&#39;s try applying this to our accordion:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useReducer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;TOGGLE_FIRST_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isFirstPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isSecondPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;TOGGLE_SECOND_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isSecondPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isFirstPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyAccordion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isFirstPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isSecondPanelOpen&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    isFirstPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    isSecondPanelOpen&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onFirstPanelClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;TOGGLE_FIRST_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSecondPanelClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;TOGGLE_SECOND_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// the rest stays the same&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View live example on CodePen: &lt;a href=&quot;https://codepen.io/HipsterBrown/details/ExZYPmM&quot;&gt;https://codepen.io/HipsterBrown/details/ExZYPmM&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Adding in our reducer helped collect all the allowed state changes into one function, rather than spreading it between two click handlers, which is useful for identifying where to add or update any new behavior. However, we still haven&#39;t solved our state explosion issue, since every new panel will add a new boolean field to our &lt;code&gt;state&lt;/code&gt; object controlled by the reducer and no guarantee the accordion couldn&#39;t end up in a state of displaying more than one open panel. We need a way to define all the allowed states for our component, i.e. a finite-state reducer;&lt;/p&gt;
&lt;p&gt;A &amp;quot;finite-state reducer&amp;quot; is just a riff on &lt;a href=&quot;https://statecharts.github.io/what-is-a-state-machine.html&quot;&gt;finite-state machine (FSM)&lt;/a&gt; as implemented using a reducer function. If we look at our earlier code, we can identify three allowed states for our component: closed, displaying the first panel, displaying the second panel. It can never be closed and displaying a panel, nor can it be displaying both panels at once. We can capture this idea by modifying our existing reducer to update a single string value and inverting the typical &lt;code&gt;switch&lt;/code&gt; statement setup.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useReducer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// state = closed | displayingFirstPanel | displaySecondPanel&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_FIRST_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_SECOND_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_FIRST_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_SECOND_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_FIRST_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TOGGLE_SECOND_PANEL&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyAccordion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isFirstPanelOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isSecondPanelOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// the rest stays the same&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View live example on CodePen: &lt;a href=&quot;https://codepen.io/HipsterBrown/details/PoWYZQm&quot;&gt;https://codepen.io/HipsterBrown/details/PoWYZQm&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The nested (or two-dimensional) &lt;code&gt;switch&lt;/code&gt; statement can be a little unsettling at first, especially when compared to the first version that appeared relatively compact. However, the point of using the current state in the first &lt;code&gt;switch&lt;/code&gt; is to describe the allowed &amp;quot;transitions&amp;quot; to another state based on the &amp;quot;action&amp;quot;, or &amp;quot;event&amp;quot; in typical machine parlance. As we read down the 2D &lt;code&gt;switch&lt;/code&gt;, when the current state is &amp;quot;closed&amp;quot;, toggling the first panel will display the first panel, and the same goes respective to the second panel.&lt;/p&gt;
&lt;p&gt;The real control comes in the cases for when one of the panels is being displayed; when the first panel is open and it is toggled, the reducer transitions back to closed; if the second panel is toggled at this point, the reducer transitions to displaying that panel. There is no check to see if the first panel is being displayed within the &amp;quot;action&amp;quot; &lt;code&gt;switch&lt;/code&gt; because the first &amp;quot;state&amp;quot; &lt;code&gt;switch&lt;/code&gt; has already confirmed that. The &lt;code&gt;isFirstPanelOpen&lt;/code&gt; and &lt;code&gt;isSecondPanelOpen&lt;/code&gt; variables are derived from the single piece of state coming from the reducer, mostly to simplify rest of the example.&lt;/p&gt;
&lt;p&gt;A bonus feature of modeling the reducer as a finite-state machine is the ability to visualize the behavior of our feature using a state diagram.&lt;/p&gt;
&lt;p&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 970.701 527.885&quot; width=&quot;1941.402&quot; height=&quot;1055.771&quot;&gt;&lt;defs&gt;&lt;style&gt;@font-face{font-family:&amp;quot;Virgil&amp;quot;;src:url(https://excalidraw.com/Virgil.woff2)}@font-face{font-family:&amp;quot;Cascadia&amp;quot;;src:url(https://excalidraw.com/Cascadia.woff2)}&lt;/style&gt;&lt;/defs&gt;&lt;path fill=&quot;#fff&quot; d=&quot;M0 0h970.701v527.885H0z&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M68.584 193.692c67.79-2.469 136.965-2.598 189.624-3.112m-190.392 1.53c65.24 1.685 129.703.792 190.028-.558m-.99 1.102c-.578 10.782 3.119 20.702 1.05 42.119m-.002-43.741c1.338 14.962 1.232 30.996-.431 41.92m-.98 1.021c-36.089 2.145-77.496.767-189.766-.423m192.23.015c-73.82.374-144.734.28-190.98 1.12m-1.247-1.66c1.634-11.53 2.717-27.935 1.769-41.83m.088 42.226c.15-16.537-1.441-31.575.335-41.661&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text x=&quot;29&quot; y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; text-anchor=&quot;middle&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(134.201 199.885)&quot;&gt;closed&lt;/text&gt;&lt;path d=&quot;M524.526 341.231c77.599-2 155.643-2.274 267.31 1.098m-266.458-.024c60.366-1.18 120.538-1.157 265.5-.492m-1.564.743c3.428 11.373 1.321 24.88 2.981 40.677m-.338-42.136c-.226 12.391-1.606 22.363-1.673 41.335m2.318.267c-98.095-.997-195.857-.986-266.125-.86m264.15-.389c-87.135-.747-176.123.393-265.165-.075m-.371 2.104c1.377-17.491.7-32.425 1.834-40.001m-.856 37.625c-1.744-9.746-.302-21.202-.99-39.115&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text x=&quot;110&quot; y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; text-anchor=&quot;middle&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(548.201 347.885)&quot;&gt;displaying second panel&lt;/text&gt;&lt;path d=&quot;M537.022 119.867c104.872.597 209.81 2.067 266.658.147m-266.057.321c85.873-1.094 172.03-.904 267.266-.245m1.745.243c-2.496 15.14-1.106 29.565-2.183 40.954m1.022-40.318c-1.068 7.575-1.255 17.913-.016 40.061m-.682.58c-82.457 1.095-161.366 1.313-266.695 1.463m267.293-1.696c-72.336-.022-143.506-1.161-267.19-.072m.368-.247c-.654-10.469.774-18.79-1.508-39.67m-.187 40.898c1.084-10.987.243-21.22 1.24-41.945&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text x=&quot;107.5&quot; y=&quot;19.402&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;21.078&quot; text-anchor=&quot;middle&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(561.201 127.052)&quot;&gt;displaying first panel&lt;/text&gt;&lt;path d=&quot;M136.044 183.761c65.9-5.38 331.247-28.024 397.239-33.579m-395.424 32.993c65.74-5.729 329.5-29.187 395.043-34.992&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M507.189 162.698c9.621-6.96 20.685-12.176 24.295-14.99m-25.171 13.73c7.932-4.56 15.525-8.668 25.637-13.192&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M505.375 142.257c10.26 1.06 22.035 3.853 26.109 5.45m-26.985-6.71c8.323 1.31 16.437 3.065 27.451 7.249&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;rotate(-6.089 1482.94 -2215.188)&quot;&gt;toggle first panel&lt;/text&gt;&lt;path d=&quot;M100.282 245.975C170 267.989 447.063 355.93 516.5 377.8m-417.132-132.3c70.202 21.568 350.04 108.222 419.476 130.371&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M490.69 376.06c5.145.126 10.498 1.203 28.142-1.985m-30.625 3.3c7.611-.639 16.541-1.026 30.645-2.328&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M496.83 356.48c3.978 4.33 8.03 9.559 22.002 17.595m-24.485-16.281c5.99 4.519 13.306 9.28 24.505 17.253&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(132.201 325.385)&quot;&gt;toggle second panel&lt;/text&gt;&lt;path d=&quot;M773.687 328.455c-4.451-26.364-22.566-131.27-27.105-157.671m26.06 156.4c-4.64-26.176-22.49-128.874-26.99-154.916&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M759.862 198.347c-4.434-8.773-10.62-18.484-14.415-24.914m14.196 24.219a373.314 373.314 0 01-14.367-25.043M739.642 201.854c2.288-10.073 2.827-20.95 5.805-28.421m-6.023 27.726c1.96-7.518 3.034-16.79 5.852-28.55&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(781.701 235.885)&quot;&gt;Toggle first panel&lt;/text&gt;&lt;path d=&quot;M594.83 178.493c3.273 25.294 15.536 125.554 18.222 150.618m-19.445-151.344c3.293 25.477 15.772 127.159 19.052 152.475&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M599.974 305.313c.585 2.298 3.2 8.306 12.798 23.652m-12.956-25.047c4.512 8.755 9.727 19.976 12.494 26.32&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M620.335 302.753c-3.897 2.937-5.752 9.506-7.563 26.212m7.405-27.607c-3.139 9.734-5.589 21.92-7.867 28.88&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;g&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(389.701 242.885)&quot;&gt;Toggle second panel&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;path d=&quot;M560.87 396.776c-1.63 10.13 76.98 55.16-9.668 60.174-86.648 5.015-429.556 5.508-510.22-30.088-80.665-35.595 22.078-152.65 26.233-183.485m492.548 152.82c-1.335 9.794 80.15 53.861-6.373 59.19-86.523 5.328-431.865 8.498-512.764-27.22-80.9-35.718 22.814-156.01 27.368-187.088&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M66.287 269.244c1.387-7.955.74-18.468 2.527-28.642m-4.351 29.575c1.021-4.646 2.548-11.367 4.092-28.474M47.86 260.214c8.143-4.757 14.231-11.97 20.954-19.612m-22.779 20.545c4.975-2.568 10.448-7.356 22.52-19.444&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(233.201 464.885)&quot;&gt;toggle second panel&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;path d=&quot;M599.846 109.02c-1.806-11.097 72.036-62.068-12.5-67.296-84.537-5.229-409.283 12.74-494.718 35.926-85.436 23.185-14.79 86.006-17.896 103.185m523.994-72.265c-1.34-11.031 75.41-60.442-9.117-65.919-84.528-5.476-412.323 9.847-498.048 33.057-85.726 23.21-13.112 88.65-16.304 106.204&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M56.61 158.62c.665 5.06 8.29 11.602 18.563 22.946m-19.459-22.25c5.513 7.281 10.488 13.426 18.988 23.244&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M75.47 150.533c-3.36 6.9.21 15.181-.297 31.033m-.598-30.336c.073 9.672-.353 18.133.127 31.33&quot; stroke=&quot;#000&quot; fill=&quot;none&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;text y=&quot;18&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;20&quot; style=&quot;white-space:pre&quot; transform=&quot;rotate(-4.514 423.316 -3051.213)&quot;&gt;toggle first panel&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;text x=&quot;168&quot; y=&quot;15&quot; font-family=&quot;Virgil, Segoe UI Emoji&quot; font-size=&quot;16&quot; fill=&quot;#adb5bd&quot; text-anchor=&quot;end&quot; style=&quot;white-space:pre&quot; transform=&quot;translate(792.701 506.885)&quot;&gt;Made with Excalidraw&lt;/text&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;If we want to reorganize the 2D &lt;code&gt;switch&lt;/code&gt; in our reducer, we can get the same results using a &lt;a href=&quot;https://xstate.js.org/docs/packages/xstate-fsm/#super-quick-start&quot;&gt;familiar object syntax&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  initial&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  states&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    closed&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_FIRST_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_SECOND_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    displayingFirstPanel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_FIRST_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_SECOND_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingSecondPanel&#39;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    displayingSecondPanel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      on&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_FIRST_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;displayingFirstPanel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string-property property&quot;&gt;&#39;TOGGLE_SECOND_PANEL&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;closed&#39;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;states&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyAccordion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// the rest stays the same&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View live example on CodePen: &lt;a href=&quot;https://codepen.io/HipsterBrown/details/zYNOqYY&quot;&gt;https://codepen.io/HipsterBrown/details/zYNOqYY&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the end, we have a clear description for how our feature works and the freedom to share the functionality with another component without including the UI or creating some specialized hook. If the feature grows, there is a maintainable structure for adding new states, transitions, and actions to accommodate those changes. As &lt;a href=&quot;https://redux.js.org/tutorials/fundamentals/part-6-async-logic#redux-middleware-and-side-effects&quot;&gt;side-effects&lt;/a&gt; start to get involved, we can reach for something like &lt;a href=&quot;https://github.com/davidkpiano/useEffectReducer&quot;&gt;&lt;code&gt;useEffectReducer&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://xstate.js.org/docs/packages/xstate-fsm/&quot;&gt;&lt;code&gt;@xstate/fsm&lt;/code&gt;&lt;/a&gt;, or &lt;a href=&quot;https://xstate.js.org/docs/&quot;&gt;&lt;code&gt;xstate&lt;/code&gt;&lt;/a&gt;, but more on that in another post.&lt;/p&gt;
&lt;p&gt;In the meantime, check out some other articles that dig into managing gradual complexity and state explosion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/mpocock1/usestate-vs-usereducer-vs-xstate-part-1-modals-569e&quot;&gt;useState vs useReducer vs XState&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/davidkpiano/redux-is-half-of-a-pattern-1-2-1hd7&quot;&gt;Redux is half of a pattern&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Simple Slot API for React</title>
    <link href="https://hipsterbrown.com/training-data/simple-slots-for-react/"/>
    <updated>Tue, 16 Mar 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/simple-slots-for-react/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Once you get started building components in React, they start out fairly simple or very specific to a feature. As you continue constructing your interfaces, patterns emerge and there&#39;s a need for more generalized, flexible components. You may stumble upon the term &amp;quot;compound components&amp;quot; through some very informative articles like &lt;a href=&quot;https://epicreact.dev/soul-crushing-components/&quot;&gt;this one from EpicReact.dev&lt;/a&gt;. To author compound components at work and for personal projects, I use an approach inspired by the &lt;code&gt;slot&lt;/code&gt; element from the Web Component spec.&lt;/p&gt;
&lt;p&gt;If you haven&#39;t heard of the &lt;code&gt;slot&lt;/code&gt; element:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a placeholder inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Slot&quot;&gt;MDN HTML elements reference &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is a more in-depth tutorial here: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This sort of composability is great when building structural components, like layouts, that should be very flexible about their content but want more control over placement of that content. In React, &lt;code&gt;props&lt;/code&gt; could certainly be used, however the readability of JSX is hindered when passing large blocks of context through &lt;code&gt;props&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the past, I&#39;ve enabled this kind of flexibility with static child component connected to a parent:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLayoutComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Header&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  Body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  Footer&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; MyLayout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;MyLayoutComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; footer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Children&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// place elements as needed&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

MyLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
MyLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;Body&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
MyLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;Footer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above approach relies on the person using &lt;code&gt;MyLayout&lt;/code&gt; to know the correct ordering of the child components. Repeating this pattern in other components would be tedious and lead to drift as each component author tries a different take.&lt;/p&gt;
&lt;p&gt;Here is a simple but conventional implementation of a &lt;code&gt;Slot&lt;/code&gt; component with an associated helper function:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SlotProps&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Slot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SlotProps&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSlots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  names&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  children&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactElement &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactElement&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNode &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactNodeArray
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReactElement &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; slot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Children&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; child &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValidElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; Slot &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; SlotProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        slot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cloneElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; slot&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the &lt;code&gt;Slot&lt;/code&gt; component itself is basically a wrapper object to expose a &lt;code&gt;name&lt;/code&gt; prop for use in the &lt;code&gt;getSlots&lt;/code&gt; function. Using this API to the earlier &lt;code&gt;MyLayout&lt;/code&gt; component would look like the following:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getSlots&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Slot &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./Slot&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; MyLayout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;FC&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; footer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSlots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;header&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;body&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;footer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// place elements as needed&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// usage&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyLayout&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello World&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Slot&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyLayout&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we wanted to provide stronger type feedback for available slot names:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; getSlots&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Slot &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./Slot&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLayoutSlot&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;header&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;body&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;footer&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyLayoutComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Slot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MyLayoutSlot&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; MyLayout&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;MyLayoutComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; footer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSlots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;header&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;body&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;footer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// place elements as needed&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

MyLayout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Slot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Slot &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; MyLayoutSlot&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even with the slight overhead of adding a static &lt;code&gt;Slot&lt;/code&gt; child, the authoring and usage of the new API provides an arguably better experience than the approach of three different static child components. There is no need to worry about ordering and any unused slot will default to &lt;code&gt;null&lt;/code&gt;, thus &lt;a href=&quot;https://reactjs.org/docs/react-component.html#render&quot;&gt;rendering nothing&lt;/a&gt;. When comparing it with the &lt;code&gt;slot&lt;/code&gt; API for Web Components, it reverses the roles for the sake of discoverability and type-safety; since the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; is used in the component template to author the slotted areas, and a &lt;code&gt;slot&lt;/code&gt; attribute is used to set the slotted content when using the component.&lt;/p&gt;
&lt;p&gt;As mentioned before, the &lt;code&gt;Slot&lt;/code&gt; component is a convenient feature for providing flexibility with control over placement of content. There is still a case for using static child components to provide styling or functionality through association without carrying about placement (maybe more on that in a future post).&lt;/p&gt;
&lt;p&gt;Maybe this feature will be built into React one day, like it is for &lt;a href=&quot;https://v3.vuejs.org/guide/component-slots.html#slot-content&quot;&gt;Vue&lt;/a&gt; and &lt;a href=&quot;https://svelte.dev/tutorial/slots&quot;&gt;Svelte&lt;/a&gt;. Until then, I hope this solution helps you as much as it has helped me.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Creating React Context types with generics</title>
    <link href="https://hipsterbrown.com/training-data/react-context-with-generics/"/>
    <updated>Thu, 11 Mar 2021 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/react-context-with-generics/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;There are several great articles describing how to work with &lt;a href=&quot;https://reactjs.org/docs/context.html&quot;&gt;React Context&lt;/a&gt; using TypeScript, including one from the folks at &lt;a href=&quot;https://reacttraining.com/&quot;&gt;React Training&lt;/a&gt;: &lt;a href=&quot;https://reacttraining.com/blog/react-context-with-typescript/&quot;&gt;https://reacttraining.com/blog/react-context-with-typescript/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this quick post, I want to describe how I&#39;ve used &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/generics.html&quot;&gt;TypeScript generics&lt;/a&gt; in combination with React Context to build more flexible but strongly typed shared state. So prior knowledge of React Context and TypeScript is assumed, otherwise select one of the previous links before continuing.&lt;/p&gt;
&lt;p&gt;For a refresher on generics:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While using &lt;code&gt;any&lt;/code&gt; is certainly generic in that it will cause the function to accept any and all types for the type of &lt;code&gt;arg&lt;/code&gt;, we actually are losing the information about what that type was when the function returns. If we passed in a number, the only information we have is that any type could be returned.&lt;/p&gt;
&lt;p&gt;Instead, we need a way of capturing the type of the argument in such a way that we can also use it to denote what is being returned. Here, we will use a &lt;em&gt;type variable&lt;/em&gt;, a special kind of variable that works on types rather than values.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Type&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Type &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/generics.html#hello-world-of-generics&quot;&gt;&lt;em&gt;https://www.typescriptlang.org/docs/handbook/2/generics.html#hello-world-of-generics&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the feature in question, there is a list of items that can be selected in a table with the selected items stored as a list of IDs. We want to make sure this Context data stores objects that must include an &lt;code&gt;id&lt;/code&gt; property. We need to tell TypeScript that the generic type argument has constraints (required fields in this case): &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints&quot;&gt;https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemWithID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyContextData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; ItemWithID&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  selectedItems&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  itemList&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Item&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gets a little tricky when creating the Context object since it is declared as a static variable:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; MyContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyContextData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ItemWithID&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generic type argument for &lt;code&gt;MyContextData&lt;/code&gt; will use the constrained &lt;code&gt;ItemWithId&lt;/code&gt; type as a placeholder. Then we can create our &lt;code&gt;Provider&lt;/code&gt; component to accept an &lt;code&gt;Item&lt;/code&gt; generic:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyProviderProps&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; ItemWithId&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  itemList&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Item&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;MyProvider&lt;/span&gt; &lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; ItemWithId&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; itemList &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PropsWithChildren&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyProviderProps&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// keep track of `selectedItems` somehow&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyContext.Provider&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; itemList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectedItems &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyContext.Provider&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get the correct generic usage for the hook that consumes that Context object, some overrides need to happen:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useMyContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; ItemWithID&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyContextData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MyContext &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyContextData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;useMyContext must be used under MyContextProvider&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The type of &lt;code&gt;MyContext&lt;/code&gt; is &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions&quot;&gt;asserted&lt;/a&gt; to match the expected type with &lt;code&gt;Item&lt;/code&gt; generic. Otherwise, TypeScript will raise an error like:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;Type &lt;span class=&quot;token string&quot;&gt;&#39;MyContextData&amp;lt;ItemWithID&gt;&#39;&lt;/span&gt; is not assignable to &lt;span class=&quot;token builtin class-name&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MyContextData&amp;lt;Item&gt;&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we can use our &lt;code&gt;Provider&lt;/code&gt; component and hook with our feature:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// import MyProvider and useMyContext&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Account&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; itemList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// request or create this data&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyProvider&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;itemList&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;itemList&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AccountTable&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyProvider&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;AccountTable&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; itemList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectedItems &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useMyContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Account&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// use the itemList and selectedItems data, which should match `Account[]` and `string[]` types respectively&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it! Once you get the hang of the pattern, it opens up a world of flexible, reusable state management in your React + TypeScript application.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>An Open Discussion About People</title>
    <link href="https://hipsterbrown.com/training-data/An-Open-Discussion/"/>
    <updated>Tue, 04 Nov 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/An-Open-Discussion/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;I had a fairly thorough but brief discussion with &lt;a href=&quot;https://twitter.com/bencallahan&quot;&gt;Ben Callahan&lt;/a&gt; of &lt;a href=&quot;http://seesparkbox.com/&quot;&gt;Sparkbox&lt;/a&gt; yesterday evening about Web Design and Development Processes, or at least it started out that way, via &lt;a href=&quot;https://docs.google.com/document/d/1M02V6mVMgFa9In-wcId4bBkCu62Ngk-sJzkljKKkBi8/edit?usp=sharing&quot;&gt;public Google Doc&lt;/a&gt;. These are some initial takeaways from that discussion and experience:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The role of front-end developer is still very vague in the responsibilities, purpose, and knowledge required to fulfill this position&lt;/strong&gt;; this includes which side the group stands on with the gap between designers and developers in a work/project environment. Ben asserts that there shouldn&#39;t be a gap, wall, or division between the teams, and we should &amp;quot;bring everyone to the table.&amp;quot; I completely agree, and I still believe the ideal mix of design-thinking and development know-how makes the front-end (and its devs) the perfect platform for bridging that gap, tearing down that wall, and eliminating that division.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It&#39;s ultimately about people.&lt;/strong&gt; We kept circling back to this point thoroughout the discussion, and it epitomizes the root of most problems in any industry, much less tech. You can put a designer, front-end developer, and a server-side developer on a project, but who makes the tough decisions and ensures the project is done correctly? Companies need &lt;strong&gt;Passionate Maintainers&lt;/strong&gt;&lt;sup&gt;TM&lt;/sup&gt;, team members who can facilitate communication among the group members, as well as contribute to the end product in a technical way. Teammates should empathize with each others&#39; challenges, not just their own, and display humility to the expertise of others as well. Ben talks about developing these ideals in his teams at Sparkbox and through the various &lt;a href=&quot;http://buildright.io/&quot;&gt;Build Right Workshops&lt;/a&gt;, but they are not easily or traditionally taught skills, especially in such a short period of time. I suppose it helps to at least start a dialogue among a group, or company, to foster those ideas when hiring new people and supporting the old. I would personally love to attend the Collaboration workshop at least once.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Community is key.&lt;/strong&gt;  As I stated in the Doc, we can look to Open Source Software (OSS) projects as a prime example of Passionate Maintainers&lt;sup&gt;TM&lt;/sup&gt;, people who care about the success of the project because it matches their ideals and is important to the community built around it. There are complete conferences around OSS projects and high-level jobs for OSS developers to represent their company in helping maintain the free tools they use everyday, because the power of a coomunity is massive. The most deciding factor behind learning and sticking with a new tool/framework/whatever is the community of people also sticking with it; it could have the strangest syntax, button layout, or name, but I will overcome those gripes as I consume the numerous articles, demos, and videos created by the community to learn all I need to know. So besides hiring someone to contribute to OSS, how does a company inspire the same type of passion and spirit seen in open source communities around its personal product or service? One the personnel level, Ben says, &amp;quot;We need to express the “bigger thing” that we want to champion and trust that the right people will show up.&amp;quot; They [Sparkbox] like to know a person well enough that they are confient that their ideals match up before hiring them; this could be contracting them for a real world project and seeing how they work, or simply becoming more familiar with them over time. I&#39;m still not sure how this can scale into the enterprise space with department heads and hundreds, if not thousands, of employees, but I didn&#39;t say we had all the answers yet.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Phew, that was a lot to download a couple hours after the initial discussion. I will probably be going over that Doc a lot and exploring these topics with other owners, developers, designers, managers, etc to get their weigh in as well. I&#39;m hoping more people contribute to it organically and inspire further discussion. I would love if anyone reading this post would submit any constructive thoughts in the Doc, especially since I don&#39;t have comments.&lt;/p&gt;
&lt;p&gt;Thanks for reading. Cheers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hipsterbrown&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; There really should be a better way to have the type of open discussion I started with Ben. Using a collaborative Doc or GitHub issue is kind of a hack for the real solution, because someone ultimately owns that issue or document, and/or the medium isn&#39;t open enough for everyone to contribute. There was a service called &lt;a href=&quot;http://branch.com/&quot;&gt;Branch&lt;/a&gt; that was pretty popular about two years ago until Facebook acquirred it and rolled it into their platform (even though the old Branches still exist). It had the nice format of conversational comments you find in GitHub issues and the accessibilty to anyone like Google Docs, but completely focused on open conversations. &lt;a href=&quot;https://www.quora.com/&quot;&gt;Quora&lt;/a&gt; is the closest alternative I can think of, but the user experience is too focused on sign-ups before any content is shown and the amount of emails received after signing up just isn&#39;t worth it. So if someone knows of any good alternatives, please let me know.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Elevatr - A Better ScrollTo Plugin</title>
    <link href="https://hipsterbrown.com/training-data/Elevtr-ScrollTo-Better/"/>
    <updated>Tue, 28 Oct 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Elevtr-ScrollTo-Better/</id>
    <category term="long-form"/>
    <content type="html"></content>
  </entry>
  
  <entry>
    <title>The Convenience Product (Problem?)</title>
    <link href="https://hipsterbrown.com/training-data/The-Convenience-Product/"/>
    <updated>Tue, 09 Sep 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/The-Convenience-Product/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;#The Convenience Product (Problem?)&lt;/p&gt;
&lt;p&gt;I&#39;ve talked before about &lt;a href=&quot;http://hipsterbrown.com/musings/musing/Design-for-Convenience/&quot;&gt;Designing for Convenience&lt;/a&gt;, referencing the Pebble smartwatch in particular as a convenience item. Then, a couple days ago, I found out about &lt;a href=&quot;https://www.indiegogo.com/projects/jibo-the-world-s-first-family-robot&quot;&gt;Jibo&lt;/a&gt;, another crowdfunded tech product that is also an item of convenience rather than necessity. Even though its developer editions won&#39;t be released for over a year from funding, I predict the evolution of this product will be very similar to how Pebble has grown in the market since its release over a year and a half ago.&lt;/p&gt;
&lt;p&gt;First, tech items of convenience. I knew from the moment I saw the Kickstarter video for Pebble that it would be an awesome companion to my smartphone and offer some great conveniences to my everyday life. It is not a device I need to communicate, travel, or stay productive day-to-day, like my smartphone; it offers me shortcuts for controlling the music on my phone, glance-able notifications, and a few other simple functions like alarms or step counters. While I feel a little weird when I leave the house without it, I can function in the real world without it because I would just need to pull out my phone to do the exact same activities. I think it&#39;s pretty clear what I mean as an item of convenience.&lt;/p&gt;
&lt;p&gt;Now, I see an item like Jibo that falls pretty clearly falls under that definition as an item of convenience due to its abilities and disabilities. Like Pebble, Jibo offers a shortcut to common smartphone functions like notifications and quick controls to things like music and cameras. Jibo also has its advantages with audio input &amp;amp; responses and an AI (Artificial Intelligence) to custom the experience for the each user. Its disadvantages are mostly portability and cost. Jibo only lasts about 30 minutes without being plugged in, which means it won&#39;t shut down while being carried around the house to wherever it is needed, so including that fact with the $600 price point and Jibo becomes a household appliance rather than a mobile device like a smartwatch. The intro video shows Jibo being used by families and single people alike, to show off its various use cases, however spending $600 on something that sits at home all day may not be as beneficial for the single life as it would for a family.&lt;/p&gt;
&lt;p&gt;Pebble and Jibo also have another large similarity that comes down to software and business. Like Pebble, the Jibo company will provide the hardware and basic software to sell to consumers, but the sustainable business model comes from extra content and applications that are purchased throughout the lifetime of the product in order to extend its functionality and usefulness. This is how Amazon is justifying their hardware products, like many other large tech hardware sellers, but there is a common Catch-22 that comes with starting this type of business. Without people to make the quality content, no one will buy your product, i.e without its fantastic community of app developers, Apple&#39;s iDevices would not have survived for as long as it has. It doesn&#39;t always follow the Field of Dreams philosophy*.&lt;/p&gt;
&lt;p&gt;*If you build it, they will come.&lt;/p&gt;
&lt;p&gt;Once Jibo&#39;s developer edition is released, there will be the exuberant group of software developers who will launch the development kit and start hacking on simple apps to test the system. Many of these apps will circulate this small group of early adopters and get the ball rolling on building an initial store of content for Jibo to promote when they start shipping the consumer editions a couple months later. As consumers start to explore this basic collection, they will blow through them quicker than developers can produce new and exciting content. Because Jibo can&#39;t sustain making all the content themselves, they will have to do what Pebble has started doing lately—working with well-known companies to integrate their products with Jibo software. For example, Pebble has worked with services like Foursquare, Runkeeper, and Evernote and companies like GoPro, ESPN,  and Mercedes to create premium, free apps to draw interest to their product.&lt;/p&gt;
&lt;p&gt;The one step I have yet to see Pebble take is making their software community profitable. There are very few paid apps or watchfaces in their store, and I don&#39;t think it&#39;s even possible to pay for apps through their store so the payments usually are usually for the companion smartphone apps. Without a profitable outcome for the developers, there is no incentive to put in the time and effort to integrate with their existing mobile apps or even create totally new ones. If Pebble, or Jibo, can&#39;t produce a profitable ecosystem for app and content creators, then sustainability is a pretty unlikely goal.&lt;/p&gt;
&lt;p&gt;I&#39;m sure the fine folks running people and the small team at Jibo are thinking about these all the time as they develop their products and businesses but I wonder how many other potential entrepreneurs do as well. I don&#39;t have any definite solutions for building a healthy developer community and booming content system around an indie tech product, but a great place to start is the open source community. There are so many open source projects that have enthusiast contributors and users because they are managed so well out where everyone can see. I take note of projects like &lt;a href=&quot;http://hood.ie/&quot;&gt;Hoodie&lt;/a&gt; and &lt;a href=&quot;http://emberjs.com/&quot;&gt;Ember.js&lt;/a&gt; and companies like &lt;a href=&quot;http://andyet.com/&quot;&gt;&amp;amp;yet&lt;/a&gt;, that all work hard to support their open source ecosystem.&lt;/p&gt;
&lt;p&gt;All in all, I&#39;m happy to see innovative and creative projects like Jibo get funded by the public because it moves tech forward. It&#39;s a very ambitious goal to ship the initial batch of this complex in a year and change, so I wish that team all the luck and look forward to the final result.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Other Side of Empathy</title>
    <link href="https://hipsterbrown.com/training-data/The-Other-Side-of-Empathy/"/>
    <updated>Sat, 05 Jul 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/The-Other-Side-of-Empathy/</id>
    <category term="long-form"/>
    <content type="html"></content>
  </entry>
  
  <entry>
    <title>The Creativity Commitment</title>
    <link href="https://hipsterbrown.com/training-data/The-Creativity-Commitment/"/>
    <updated>Fri, 20 Jun 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/The-Creativity-Commitment/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;If there was any downside to not being in school anymore, it might be that there is less external motivation to create anything anymore. As much disdain I may have had towards those reports and group projects, they were new things I produced by my own design; although I&#39;ve never had issues pursuing personal side projects either. So, without push of higher education, I have come up with a guideline to keep up some creative consistency.&lt;/p&gt;
&lt;p&gt;I have made a commitment to creativity in my post-collegiate life, and convinced my roommates to join me in this endeavor. There is only one rule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;create something on your own time, once a week
It&#39;s a simple task, which can lead a superb feeling of accomplishment after seeing how much you&#39;ve done over a short amount of time. For example, I live with two English graduates—two people who spent 4 years producing more written work than I ever did in my entire educational experience, so this commitment should be a piece of cake. They can outline a story, craft of poem, or even write a book report, but they are not tied to their literary leanings. Just as long as they are producing something they can be proud of, I will be proud of them.&lt;/p&gt;
&lt;p&gt;For myself, I have sought diversity in my creative endeavors. Since it has to be on my own time, none of the professional work I do can count towards my submission each week; so I&#39;ve chosen to pursue different outlets such photographing my new neighborhood, contributing to open source projects, sketching potential side projects and perhaps hacking them together on occasion, and also keeping up with my roomies in the writing department through this blog.&lt;/p&gt;
&lt;p&gt;Some weeks I may end up with a dozen creations to share. Other weeks I will have to force myself to keep up with my commitment because I know I will appreciate in the end. Sharing this pact with others will help when internal motivation is just not enough, as well as validate the work we do together. I look forward to witnessing what we can accomplish over the next year and beyond.&lt;/p&gt;
&lt;p&gt;Thus, with the publishing of this post, I have submitted my contribution for the week.&lt;/p&gt;
&lt;p&gt;Thank you for reading. Now go and make something.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Apples &amp; Oranges</title>
    <link href="https://hipsterbrown.com/training-data/Apples-and-Oranges/"/>
    <updated>Wed, 11 Jun 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Apples-and-Oranges/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;It&#39;s been over a month since I graduated from college, and now I&#39;ve made one of the biggest changes in my life by moving to New York City after living in Florida for so long.&lt;/p&gt;
&lt;p&gt;I have grown so much after living in Orlando for nearly four years. Half of that came from my first two years of intense involvement with various organizations and UCF. The other half came from reaching out into the Orlando community and the connecting with the people &amp;amp; businesses there. It was that most recent bit of growth that really inspired me to reach out further and join one of the largest tech communities in the country. It was people like Dan Denney, Robb Schiller, Roman Rusinov, Mark Bunker, and really everyone at Envy Labs who motivated me to keep exploring and connecting to the community at large. Events like Design Orlando, Orlando Tech Meetup, Front-End Orlando, Node Orlando, Startup Weekends, and the Front-End Design Conference showed me how great it was to see people coming together to discuss things they care about and push the web forward. I will miss those people and events, but I will not forget them or where I started in the City Beautiful. All I can really say is thank you, I will return one day.&lt;/p&gt;
&lt;p&gt;I moved to New York purely for an adventure; I already have a remote job, so I could have moved nearly anywhere without worry of income. But I chose New York because of the ability to explore the rich community and culture inside the city and the surrounding areas, especially through the ease of public transportation. I chose New York because I have lived in Florida my entire life and wanted a change of scenery while I am still free from physical ties to one location. I chose New York because of its history of residence who innovated and persevered through whatever hard times came their way. Living so far away from Florida reduces my safety net of staying home until I find something more secure; it forces me to take on the world right away. It will come with new challenges, but it will also come with new meetups, conferences, opportunities, and lessons I can learn from. Frankly, I&#39;m still a little shocked that I&#39;ve made it, even after 6 months of planning.&lt;/p&gt;
&lt;p&gt;New York is just one stop on the road ahead, however. As I&#39;ve said before, I&#39;m at that time in my life where I can explore so freely. I believe the best way to do that is to live in an area for a few years, rather than just visit for a few days. After two or three years, I may want to live somewhere in Europe like England, Germany, or Switzerland. My love for the web and tech grows as I discover there are so many places I can go and keep working to make awesome stuff for it. I will not plan too far ahead because I have no clue what may come but I am ready to adapt and keep on creating.&lt;/p&gt;
&lt;p&gt;Keep an eye here as I&#39;ll be writing a lot more ( that&#39;s the plan at least) and posting links on Twitter. Thanks for following.&lt;/p&gt;
&lt;p&gt;Stay Hip.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>2014, My Year of Workflows</title>
    <link href="https://hipsterbrown.com/training-data/Year-Of-Workflows/"/>
    <updated>Mon, 27 Jan 2014 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Year-Of-Workflows/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Workflow and Process are two rapidly growing subjects in the past year as tools like Grunt became more powerful and GitHub grew to the max.&lt;/p&gt;
&lt;p&gt;I wanted to write about efficient workflows for my first post of 2014, because I’ve been reading JavaScript Application Design&lt;sup&gt;1&lt;/sup&gt; in its early stages. It discusses the use of Grunt and other techniques to create a more streamlined workflow of developing modern web applications with JavaScript. Even Chris Coyier has done screencast covering this topic on CSS-Tricks&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;After giving some thought to what I wanted to accomplish this year, I decided to go fairly general and say I want to improve the workflows of all the tasks and chores I handle day-to-day, I want to streamline my daily tasks the same way I would streamline my various web projects.&lt;/p&gt;
&lt;p&gt;Even as I write this post, I am thinking about how I could efficiently place links for greater reader retention and easier comments for later editing in Byword.&lt;/p&gt;
&lt;h2&gt;Photo and Data Management&lt;/h2&gt;
&lt;p&gt;This is a huge area of concern for me because I tend to download so many things and forget which folder/directory/backup I placed them in, or, worse, I leave it all in my Downloads folder like the junk drawer in my kitchen.&lt;/p&gt;
&lt;p&gt;So when I bought a new computer last month, I made a conscious effort to create a better directory structure with clear names for easy eye-scans through Finder.&lt;/p&gt;
&lt;p&gt;I also have to thank Dan Denney for showing me the awesome power of Alfred&lt;sup&gt;3&lt;/sup&gt; for quick-launching applications,  opening files, and solving simple math bits. I know there are more hardcore users who take advantage of Alfred&#39;s workflows, but I&#39;ve yet to dive that deep into the tool.&lt;/p&gt;
&lt;p&gt;I was storing a lot of this data so I could access it anywhere, and was quickly running out of space, using all the free options in the various popular cloud solutions like Dropbox, SkyDrive, and Google Drive. But, being the cheap bastard that I am, I didn&#39;t really want to dish out the monthly cash to access only a fraction of what I needed.&lt;/p&gt;
&lt;p&gt;Luckily, my slight Kickstarter addiction saved the day, as I discovered a project that seemed to solve my problem perfectly. Space Monkey&lt;sup&gt;4&lt;/sup&gt; aims to simplify cloud storage and make it more affordable for the common man by spreading the servers to the people as nifty little desktop devices. After purchasing the device, for the price equivalent of 20 months of 100GB of Dropbox storage, I have 1TB (that&#39;s 1000GB for those counting at home) for $5/month after the first year. Even though the software behind the device is still in its basic stages, it&#39;s improving all the time and super convenient. That&#39;s just my little unofficial plug for them, I look forward to seeing how this product improves over the next year.&lt;/p&gt;
&lt;h2&gt;Design and Development&lt;/h2&gt;
&lt;p&gt;Since my livelihood is dependent on how well I create for the web and beyond&lt;sup&gt;TM&lt;/sup&gt;, I&#39;m always thinking about how I can streamline my work processes.&lt;/p&gt;
&lt;p&gt;Entering 2014, there are a ton of great tools and services across the internet to help simplify and automate all the menial tasks we do as designers and developers. Whether you&#39;re compiling, concatinating, compressing, linting, or even deploying code, tools like Grunt &amp;amp; Gulp make life so much easier and allow web workers to spend more time focusing on writing code.&lt;/p&gt;
&lt;h3&gt;side note&lt;/h3&gt;
&lt;p&gt;I was fortunate enough to start a new job&lt;sup&gt;5&lt;/sup&gt; that dumped me into the Grunt pool at the beginning of the year, so I learned by doing on a full project (which is really the best way to truly learn anything in my opinion), but you don&#39;t have to take the plunge that way. My favorite resource so far has been Chris Coyier&#39;s 24ways article&lt;sup&gt;6&lt;/sup&gt;—plus the Grunt community is growing so fast that there is bound to be some friendly dev out there willing to anwer any questions.&lt;/p&gt;
&lt;p&gt;Along with Grunt, I have made an effort to conquer my fear of the command line and the great tools, like git, that come with mastering it. Starting slow with a few basic bits of version control like starting a local git repository for a personal project and moving on to pushing my work to a remote repo on GitHub. Now if the past sentence sounded like a Geek to you, check out Try Git&lt;sup&gt;7&lt;/sup&gt; from the good people at Code School. Leveling up from there would involve checking out git hooks for simple deployment to a staging server before pushing to production, all the stuff of an efficient Front-End Operations Engineer&lt;sup&gt;8&lt;/sup&gt;.  It may seem like a lot to do right away but remember it&#39;s just the beginning of 2014, so take a deep breath and take your time.&lt;/p&gt;
&lt;p&gt;As for my design workflow, I continue to be satisfied by my experience with Sketch&lt;sup&gt;9&lt;/sup&gt; for mocking up various designs or creating icons and other design assets. Anyone who has ever spoken to me about my static design creation knows that I have been enamored by Sketch since I started using it about a year ago. It&#39;s extremely cheap compared to industry standard applications (&lt;em&gt;cough&lt;/em&gt; Creative Cloud &lt;em&gt;cough&lt;/em&gt;) and it&#39;s developed with the modern web worker in mind—it has the ability to export CSS stylings and awesome font rendering (looking at you Photoshop). The best part is the fact that it&#39;s continually being improved upon by the small team at Bohemian Coding, so if there is a problem, just ask and there is a good chance they&#39;ll work on it. But I digress—so my only wish in this area of my workflow is to get better at using Sketch for illustration purposes and organizing my files once they&#39;re exported, and that mostly relies on my personal discipline (more on that in a bit).&lt;/p&gt;
&lt;h2&gt;Task Management&lt;/h2&gt;
&lt;p&gt;This is another huge area for improvement because I&#39;m finishing up my final semester of college while working a full-time remote job and finding time for personal projects too. I&#39;ve used several iOS apps in attempt to solve my tasking needs but nothing seemed to be the right fit, until I tried out Trello&lt;sup&gt;10&lt;/sup&gt;. Trello has the convenience of editing my boards of tasks from anywhere via the web or mobile apps and allows me to collaborate on boards with my team for our client work. Best of all, it&#39;s completely free to use. I&#39;ve even started using it to store an inventory of my groceries to plan my meals for the week and keep track of what needs to be picked up from the store.&lt;/p&gt;
&lt;p&gt;But even with all the reminder notifications, it&#39;s ultimately up to me to follow these personal deadlines with extreme discipline. There is so much to distract me from completing a school assignment, blog post, or work design—especially since it&#39;s all done online. The latest design/development/tech article is constantly streaming through my Twitter Timeline, Netflix is just a few clicks away with a whole slew of new titles for 2014, and Steam sits on my dock begging to be opened for a couple rounds of Team Fortress 2—such is the wonder and the danger for anyone who spends the brunt of their time online. So keeping myself focused on the task at hand will be the greatest test of my commitment to the Year of Workflows.&lt;/p&gt;
&lt;h2&gt;Money Management&lt;/h2&gt;
&lt;p&gt;As a young adult preparing to leave his last college apartment for the real world come in May, money management is vital. Not that I spend my weekly wage and student loans willy-nilly on extravagant nights out or weekend trips, but there is still quite a bit I can do to keep control of my spending habits. I&#39;ve attempted this mission a few times in my college career by trying out tools like Mint and BillGuard. Mint just had too much going on for a person with zero credit cards, investments, and monthly bills besides rent, and BillGuard had a fine iOS interface but the web side was sorely lacking. Thanks to the open thought process of Marco Arment doing his taxes, I stumbled upon the free accounting apps of Wave&lt;sup&gt;11&lt;/sup&gt; that were just right. They also have things like invoicing and payroll, but all I needed was something simple for my personal finances, so Wave seemed perfect at first glance. So far, it&#39;s been a pretty enjoyable experience from the web app and mobile receipt tracking app for when I use cash instead of my debit card. It helped me set up a straight-forward budget and lets me verify each transaction before they are added to the monthly total. I&#39;ve enjoyed it so much, I&#39;ll probably end up using their invoicing app should I ever take on any freelance work.&lt;/p&gt;
&lt;h2&gt;Site and Musing Management&lt;/h2&gt;
&lt;p&gt;This has been a topic sitting in the back of my mind since I finished re-building my site last spring (a common curse of a web designer to always be improving). At the moment, my site is mostly static with minimal PHP for templating and the blog is powered by the super lightweight AnchorCMS&lt;sup&gt;12&lt;/sup&gt;. But I&#39;ve gotten tired of PHP for personal projects, especialy once I was caught up in the KickStarter campaign for Ghost&lt;sup&gt;13&lt;/sup&gt; and saw the awesome conveniece of Jekyll&lt;sup&gt;14&lt;/sup&gt; for super simple static site generation. So as soon as I find the free time, I will working on rebuilding the static side of my site using Jekyll templates. A primary driving force behind this decision is to manage my portfolio a bit easier. At the moment, the items in my portfolio link to an external resource, but I would rather have a more in-depth explaination within my site, thus Jekyll is my chosen solution. My decision to use Ghost for my blogging efforts lies in its JavaScript DNA, lightweight nature, and consistent development. I&#39;ve really enjoyed following the Ghost team and look forward to the plans they have to make Ghost the best blogging platform out there.&lt;/p&gt;
&lt;p&gt;I will attempt to make the transition to these new frameworks/platforms as smooth as possible for the millions/tens of visitors that enjoy my Hip Musings&lt;sup&gt;TM&lt;/sup&gt; every week but things will certainly be bumpy at some point. I plan to write about my experience nearly every step of the way. Stat Tuned!&lt;/p&gt;
&lt;h2&gt;Wrap Up TL;DR&lt;/h2&gt;
&lt;p&gt;As I continue to charge confidently through 2014, I am making my vow here and now to stick to the life of workflows to streamline my management of money, data, photos, projects, and time.  If you have some awesome workflow tools or unique strategies to stay focused, please reach out to me on Twitter @HipsterBrown or email me from my contact page.&lt;/p&gt;
&lt;h3&gt;Links for Days:&lt;/h3&gt;
&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; &lt;a href=&quot;http://bevacqua.io/buildfirst?utm_content=buffer0df77&amp;amp;utm_medium=social&amp;amp;utm_source=twitter.com&amp;amp;utm_campaign=buffer&quot;&gt;JavaScript Application Design&lt;/a&gt;
&lt;sup&gt;2&lt;/sup&gt; &lt;a href=&quot;http://css-tricks.com/video-screencasts/124-a-modern-web-designers-workflow/&quot;&gt;A Modern Web Designer&#39;s Workflow on CSS-Tricks&lt;/a&gt;
&lt;sup&gt;3&lt;/sup&gt; &lt;a href=&quot;http://www.alfredapp.com/&quot;&gt;Alfred&lt;/a&gt;
&lt;sup&gt;4&lt;/sup&gt; &lt;a href=&quot;https://www.spacemonkey.com/&quot;&gt;Space Monkey&lt;/a&gt;
&lt;sup&gt;5&lt;/sup&gt; &lt;a href=&quot;http://hipsterbrown.com/anchor/anchor-cms-0-2.9/index.php/posts/its-all-who-you-know&quot;&gt;It&#39;s All Who You Know&lt;/a&gt;
&lt;sup&gt;6&lt;/sup&gt; &lt;a href=&quot;http://24ways.org/2013/grunt-is-not-weird-and-hard/&quot;&gt;Grunt for People Who Think Things Like Grunt are Weird and Hard&lt;/a&gt;
&lt;sup&gt;7&lt;/sup&gt; &lt;a href=&quot;https://www.codeschool.com/courses/try-git&quot;&gt;Try Git&lt;/a&gt;
&lt;sup&gt;8&lt;/sup&gt; &lt;a href=&quot;http://www.smashingmagazine.com/2013/06/11/front-end-ops/&quot;&gt;Front-End Ops&lt;/a&gt;
&lt;sup&gt;9&lt;/sup&gt; &lt;a href=&quot;http://www.bohemiancoding.com/sketch/&quot;&gt;Sketch by Bohemian Coding&lt;/a&gt;
&lt;sup&gt;10&lt;/sup&gt; &lt;a href=&quot;https://trello.com/&quot;&gt;Trello&lt;/a&gt;
&lt;sup&gt;11&lt;/sup&gt; &lt;a href=&quot;https://www.waveapps.com/&quot;&gt;Wave&lt;/a&gt;
&lt;sup&gt;12&lt;/sup&gt; &lt;a href=&quot;http://anchorcms.com/&quot;&gt;AnchorCMS&lt;/a&gt;
&lt;sup&gt;13&lt;/sup&gt; &lt;a href=&quot;https://ghost.org/&quot;&gt;Ghost - Just a blogging platform&lt;/a&gt;
&lt;sup&gt;14&lt;/sup&gt; &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>It&#39;s All Who You Know</title>
    <link href="https://hipsterbrown.com/training-data/All-Who-You-Know/"/>
    <updated>Tue, 24 Dec 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/All-Who-You-Know/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Good news everyone! I finally found a full-time job as a front-end developer, starting in 2014. It&#39;s been quite a hectic semester for me, looking for some sort of employment in the industry before I graduate next May, and I am happy to say I found the perfect job to get started. After all the fuss over perfecting my résumé, fine-tuning my portfolio, and sending out the most personalized cover letters after scouring &lt;a href=&quot;http://www.authenticjobs.com/&quot;&gt;AuthenticJobs&lt;/a&gt; for hours at a time, I was surprised to discover it actually was, as the title implies, who I knew that finally landed me a job. I honestly believe it&#39;s the best way to find employment for almost anyone in any industry.&lt;/p&gt;
&lt;p&gt;Getting to know people within my chosen industry started with Twitter and a bit of Googling. I began following local web agencies and consulting shops, who would tweet about upcoming meetups and events discussing the various aspects of the industry. So I would attend those events, like &lt;a href=&quot;http://startupweekend.org/&quot;&gt;Startup Weekend&lt;/a&gt;, and meet like-minded folks who enjoyed creating awesome experiences for the web and beyond. Ever since last November, my involvement with the web community at large has snowballed into attending my &lt;a href=&quot;http://frontenddesignconference.com/&quot;&gt;first conference&lt;/a&gt;, where I met the most amazing designers and developers like Dave Rupert and Chris Coyier, and giving my first design talk next month. Every experience has further cemented my passion for being a part of the infinite collective that is the web.&lt;/p&gt;
&lt;p&gt;Over the past year I have done many things to improve upon my hard skills as a designer and front-end developer, including rebuilding my entire personal site and blog (with plans for some tweaking coming soon), but my &lt;a href=&quot;http://en.wikipedia.org/wiki/Soft_skills&quot;&gt;soft skills&lt;/a&gt; have also aided me even more in making numerous connections that evertually led to my current employment. The projects found in my portfolio, GitHub, and CodePen are all great for proving that I can complete the tasks required by prospective employers; however, it&#39;s the words of my acquaintances in the industry that are more powerful than pictures and demos when it comes to finding the perfect position with a company.&lt;/p&gt;
&lt;p&gt;So as the new year draws closer, I am so very thankful and appreciative for everyone I have had the chance to interact with during the last 12 months. I know each person has influenced, in one way or another, how I proceed with my chosen profession, and I look forward to meeting many more people in 2014 as I graduate with big plans ahead.&lt;/p&gt;
&lt;p&gt;Take action, make friends, and keep in touch because you never know where that connection may lead you.&lt;/p&gt;
&lt;p&gt;If you want to stay in touch with me, you can just mention me on &lt;a href=&quot;https://twitter.com/hipsterbrown&quot;&gt;Twitter&lt;/a&gt; or shoot me an &lt;a href=&quot;http://hipsterbrown.com/connect.php&quot;&gt;email&lt;/a&gt; anytime.&lt;/p&gt;
&lt;p&gt;Cheers and Happy Holidays!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Remote Work Confirmed</title>
    <link href="https://hipsterbrown.com/training-data/Remote-Work-Confirmed/"/>
    <updated>Tue, 10 Dec 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Remote-Work-Confirmed/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;##My Remote Background
I started doing remote work without realizing it until after the job was done. I was doing the brunt of writing for a small science and technology news site called &lt;a href=&quot;http://gizmocrazed.com/&quot;&gt;Gizmocrazed&lt;/a&gt;. It was an English site but the founder was based in Pakistan, so all communication was done via email or What&#39;s App (my first time using it) and at odd hours of the night or morning due to the 10 hour time difference between us. All in all, it was an exciting new experience of working wherever and whenever I felt like, as long as the work got done. My previous work experience was very hands-on and location-dependent as a food, retail, and university orientation employment usually is. Sure the pay was less than mediocre but it was worth so much more because it showed me a new path of independent employment, and I could look beyond the area around my campus for a part-time job.&lt;/p&gt;
&lt;p&gt;From there my remote work experience snowballed as I started working as a campus representative for that fruit-filled tech company over in Cupertino, which was definitely a much more official form of employment. I was classified as a remote corporate employee (complete with badge access)— I set my own hours, meetings, and whatever else was required to complete my job. It was by every definition-as found in the 37signals book-a remote job, complete with face-to-face video meetings, WebEx conference calls, and trips to corporate for some hands-on collaboration and training.&lt;/p&gt;
&lt;p&gt;During that time, I took on another job where I was not a remote worker—instead, I aided in the remote process by setting up technical resources for a construction company&#39;s managers all around the state. Being on that side of the process made it clear just how much work goes into keeping the remote operation smooth and successful. It also made clear how terrible commuting can be when the first part of your morning is taken up lethargically moving through traffic for 45 minutes, only to experience it again after 8 hours sitting at a desk.&lt;/p&gt;
&lt;p&gt;Even my education had a remote component to it, especially during the latter part. Attending a university with a boasted population of around 60,000 students is great for the variety in the vast community, but it also means large, packed, video-streamed classroom or soley online with no face-to-face interaction with the professor or other students. The advantage to the e-classroom environment was the ability to work from anywhere, as it is with most remote work; however, as it is with university education, it was easy to ignore the responsiblities of an online class when it wasn&#39;t a particularly intersting subject but required in order to  graduate. Online education tools aren&#39;t always as cutting edge as those for the modern remote worker, which makes the task a little less convenient as well.&lt;/p&gt;
&lt;p&gt;It&#39;s is clear that I&#39;m no stranger to most aspects of remote working. I recognize the advantages and disadvantages of working away from the main office, although I believe there is a heavier weight to the pros side of the eternal argument. Comfort, trust, and retention are all strong attributes for a successful and productive working environment when the alternate is considered and empowered. But everything is to be taken with a grain of salt, as no one person&#39;s perfect working situation can reflect the rest of the population. So choose remote work because you can, not because it&#39;s popular or you have to.&lt;/p&gt;
&lt;p&gt;##Remote, Office Not Required — A Review
I pre-ordered Remote when the call initially went out on Twitter from Jason Fried, mostly because I enjoyed their first book - Rework - so much. I&#39;m no stranger to waiting for a product to be fullfilled, as my Kickstarter profile can probably attest, but waiting for Remote to be released was nearly unbearable as the subject of book was becoming wildly popular and controversial.&lt;/p&gt;
&lt;p&gt;I was such a fan of the layout of teaching Rework employed, it was great to see Jason and DHH continuing that style in Remote.  The entire book reads like a collection of blog posts accompanied by unique, brilliant illustrations done by &lt;a href=&quot;http://rohdesign.com/&quot;&gt;Mike Rhode&lt;/a&gt;. Chapters are filled with sub-chapters focused on a new aspect of their parent subject. Having numerous real-world examples and stories kept my interest and clarify how various people experience remote work in their unique way.&lt;/p&gt;
&lt;p&gt;It is certainly a book one could easily finish in a day or two, although I took my time traversing through its contents, but it is also a resource anyone could just flip through randomly and learn something, i.e. the perfect coffee table book.&lt;/p&gt;
&lt;p&gt;Overall, the book covers the subject of remote work pretty completely, pros and cons, and serves as a great tutorial for anyone looking to get into that style of employment. And for the holiday season, a smart gift from to your boss if you think remote work is right for your current workplace. You can pick it up &lt;a href=&quot;http://www.amazon.com/Remote-Office-Not-Required/dp/0804137501&quot;&gt;on Amazon&lt;/a&gt; in hardback and ebook. I highly reccomend picking up a copy, even just to fill a space on your bookshelf.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Design for Convenience - A Pebble App Design</title>
    <link href="https://hipsterbrown.com/training-data/Design-for-Convenience/"/>
    <updated>Wed, 06 Nov 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Design-for-Convenience/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Pebble apps started off as fun and cheap tricks for a few minutes of amusement—now, that the platform is evolving, and it&#39;s time for those apps to start evolving too. This blog post serves as my personal guide on what Pebble app makers should consider when creating fantastic experiences for this new medium on their wrists. I have even included a proof-of-concept Pebble app design combining my favorite smartwatch and &lt;a href=&quot;http://tapbots.com/software/tweetbot/&quot;&gt;Twitter client&lt;/a&gt; to create Pebblebot, a companion app for powerful Twitter notifications.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Concept_Preview%402x.png&quot; alt=&quot;Pebblebot Icon&quot; style=&quot;width: 200px&quot; /&gt;
&lt;p&gt;The adorable Pebblebot icon&lt;/p&gt;
&lt;p&gt;##What&#39;s Your Watch For?&lt;/p&gt;
&lt;p&gt;Ever since I first got my Pebble in the mail, even before then, I&#39;ve kept tabs on what awesome app ideas were being shared and created by the great community surrounding this product. Then I started using some of the basic ones that were being shared through sites like &lt;a href=&quot;http://www.mypebblefaces.com/&quot;&gt;My Pebble Faces&lt;/a&gt; and &lt;a href=&quot;http://pebblebarn.com/&quot;&gt;Pebble Barn&lt;/a&gt;, but most of those apps stayed on my Pebble for about a week or less because I had no need for a counter, timer, or magic 8-ball app to constantly reside on my watch. After the release of httpebble, app developers could now communicate information from iOS and Android smartphones to Pebble and create &amp;quot;smart status&amp;quot; applications. It sounded great to always have the current weather, stock price, battery life, and the time available without taking your phone out of your pocket! Yet, I still went back to a standard watch face and music control app for day to day use of my Pebble because I constantly needed to check if my Pebble was still connected to the third-party iOS apps. The thing that was really missing from all these third-party applications was thoughtful, emotional design—so I set out to discover what that meant for that tiny screen hanging out on nearly 200,000 wrists worldwide.&lt;/p&gt;
&lt;p&gt;The first thing I realized was that Pebble is a device of convenience. Its users don&#39;t need it—they can get all the same tools and information from the smartphone sitting in their pocket. They bought a Pebble so they could control their music or see the latest phone notification &lt;em&gt;conveniently&lt;/em&gt; from their wrists—because that power over their mobile devices felt so magical and unique. That is the exact same emotion and convenience that needs to be evoked in the design of other Pebble apps. This initial revelation led to the idea for Pebblebot, giving its users more control over their Tweetbot notifications but nothing that couldn&#39;t be done by pulling out their phones and opening the app.&lt;/p&gt;
&lt;p&gt;##Good Design is Invisible&lt;/p&gt;
&lt;p&gt;If you&#39;re a designer, you&#39;ve heard that phrase at least once in your career and it&#39;s an intense philosophy to follow when drawing your ideas in Sketch or Photoshop— it is a philosophy of convenience and letting the design get out of the user&#39;s way. This was the path I took when creating Pebblebot—it would be invisible to the user until it was needed. Some of the inspiration for this idea came from the RunKeeper app embed within the Pebble firmware. It only comes up when you start a run with the iOS app and hides again as soon as you&#39;re done with it. It adds the convenience of knowing your current pace and time, as well as being able to pause the activity without taking your phone out of your pocket, backpack, or armband.&lt;/p&gt;
&lt;p&gt;Pebblebot would be an in-app purchase for Tweetbot for iPhone customers who want an equally awesome Twitter experience on their Pebbles. Unlike the Twitter apps that currently exist in the smartwatch ecosystem, it would not just show the latest 5 or less tweets on screen because that means the user had to go through at least 2 button presses and press a few more buttons when they wanted to refresh the feed. Pebblebot is all about notifications and giving the user to do something with them just as soon as they receive them.&lt;/p&gt;
&lt;p&gt;For example, I get a Mention from someone and this would show up:&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/PB_Mention%402x.png&quot; alt=&quot;Pebblebot Mention Screen&quot; style=&quot;width: 300px&quot; /&gt;
&lt;p&gt;From that screen, I can instantly Retweet or Favorite that Mention without ever touching my phone. Should I choose to Reply to the Mention, I can press the middle button to activate an alert asking me open my phone, taking me straight to the Tweetbot Reply screen. All three actions available to me can also be done by taking my phone out of my pocket and using the Tweetbot for iPhone app, however, I saved a few seconds and a little hassle by using my &lt;em&gt;convenient&lt;/em&gt; watch app. Other users may be fine just seeing a notification from Twitter and waiting until later to respond in some way but instant gratification is the popular stigma in this day and age.&lt;/p&gt;
&lt;p&gt;##Keep it Simple Stupid&lt;/p&gt;
&lt;p&gt;The greatest thing about Pebblebot is its utter simplicity in design. It&#39;s mostly a text-based interface with a few recognizable icons to symbolize its essential functionality and that is only when it is needed. With the notifications that one of your tweets has been Favorited or Retweeted, the screen is nearly identical to a normal Pebble notification:&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/PB_Fav%402x.png&quot; alt=&quot;Pebblebot Fav Screen&quot; style=&quot;width: 300px&quot; /&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/PB_RT%402x.png&quot; alt=&quot;Pebblebot RT Screen&quot; style=&quot;width: 300px&quot; /&gt;
&lt;p&gt;These screens are designed for maximum readability and shows all necessary information but needs no additionally functionality like the Mention screen.&lt;/p&gt;
&lt;p&gt;Also keeping to the theme of simplicity is the fact that none of settings for the watch app is found on the watch, rather all of that control is held within the Tweetbot app settings, including the ability to buy the Pebblebot. Because Pebblebot is a notification app, it is logically controlled under Tweebot&#39;s Notifications settings as shown below:&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/TB_BuyBanner%402x.png&quot; alt=&quot;Pebblebot Banner Screen&quot; style=&quot;max-width: 30%; min-width: 300px;&quot; /&gt;
&lt;p&gt;Once Pebblebot is bought and downloaded to the users smartwatch, they will be taken back to the Notifcations screen again to ensure their Pebble is connected to Tweetbot and given additional controls for that functionality.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/TB_Notifications%402x.png&quot; alt=&quot;Tweetbot Notifications Screen&quot; /&gt;
&lt;p&gt;The user&#39;s Tweetbot notifications can remain independent of their Pebblebot notifications, just as Pebble&#39;s notifications can remain separate from your phone. The ultimate goal of staying simple and beautiful is always kept in mind.&lt;/p&gt;
&lt;p&gt;##A Little Something Extra&lt;/p&gt;
&lt;p&gt;During the creation of this app concept, I thought about what feature could make Pebblebot a truly powerful and useful watch app for the die-hard Twitter users. The idea for this added functionality stemmed from a feature that has been a part of Twitter for a while, albeit poorly implemented on their side. The final feature of Pebblebot you may have seen in the Tweetbot screens above—presenting the Pebblebot SuperFollower notification, a.k.a stalker mode.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/PB_SuperFollow%402x.png&quot; alt=&quot;Pebblebot Super Screen&quot; style=&quot;width: 300px&quot; /&gt;
&lt;p&gt;Just like the Mention screen, the user is given the ability Favorite, Reply, or Retweet anyone they choose to stalk/SuperFollow via their Pebble. Twitter has tried the same thing via text messages or their own mobile apps, however the settings and control over that feature is complex for most users to figure out. In Tweetbot, once you have connected to Pebblebot, you can go to any user&#39;s profile and have to ability to SuperFollow them in the same action as you would follow them normally in the app. Shown below:&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/TB_Profile%402x.png&quot; alt=&quot;Tweetbot Profile Screen&quot; style=&quot;max-width: 30%; min-width: 300px;&quot; /&gt;
&lt;p&gt;Then, should you grow tired of that user&#39;s constant Walking Dead live tweeting, you don&#39;t have to navigate back to their profile—instead, you can go to your Notification Settings and tap SuperFollowings:&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/TB_SuperFollow%402x.png&quot; alt=&quot;Tweetbot SuperFollow Settings Screen&quot; style=&quot;max-width: 30%; min-width: 300px;&quot; /&gt;
&lt;p&gt;Through a simple swipe gesture you can temporarily disable a user&#39;s SuperFollow notifications or remove them entirely from the list. This is a much better way to control this feature, rather than going through an entire following list trying to remember which once you&#39;ve chosen to SuperFollow.&lt;/p&gt;
&lt;p&gt;##Wrap-Up&lt;/p&gt;
&lt;p&gt;Overall, Pebblebot is the best representative for how Pebble can be integrated with the powerful services already found on our smartphones. It is not trying to reinvent the wheel by replacing these services—it just turbo-charges the vehicle for a little added fun. Pebble is &lt;a href=&quot;https://developer.getpebble.com/&quot;&gt;already doing great things by working with third-party services&lt;/a&gt; and improving the SDK for better two-way communication. It all makes me so excited for the future of this medium, and I hope that Pebblebot can serve as an example for other Pebble app creators to follow for their next development project.&lt;/p&gt;
&lt;p&gt;Finally, Pebblebot is merely a figment of my imagination. I have neither the skills or resources to make such a product possible, but if you really wanted this watch app to exist, be sure to bug @Tapbots_Paul, @MarkJardine, and @Pebble on Twitter and show them this article. Or you can just share it for the sake of sharing. I would love to hear anyone&#39;s thoughts on this concept in the comments below, on Twitter (@HipsterBrown), or via email from my Contact page.&lt;/p&gt;
&lt;p&gt;Cheers and thanks for reading this post, or at least looking at all the pretty pictures. Stay hip.&lt;/p&gt;
&lt;p&gt;P.S.&lt;/p&gt;
&lt;p&gt;I&#39;ve added my slides from my talk at &lt;a href=&quot;http://designorlan.do/&quot;&gt;Design Orlando&lt;/a&gt; to SpeakerDeck &lt;a href=&quot;https://speakerdeck.com/hipsterbrown/designing-for-convenience&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Visual Design in the Browser</title>
    <link href="https://hipsterbrown.com/training-data/Visual-Design-in-Browser/"/>
    <updated>Thu, 17 Oct 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Visual-Design-in-Browser/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;There is a style of workflow in web design called designing in the browser&lt;sup&gt;&lt;a href=&quot;https://hipsterbrown.com/training-data/Visual-Design-in-Browser/#one&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; that has designers re-thinking the way they do their job. The basis of this idea is to skip the wireframes and static mockups in Photoshop, Sketch, etc, and start directly with code—usually within the console of the web designer&#39;s preferred browser. Some of the greatest advantages of following this philosophy is saving time, keeping the design process DRY (don&#39;t repeat yourself), flexible &amp;amp; responsive environment, and instantaneous prototype testing. So while the wireframes and UI design responsibilities is has been passed on to the browser, tools like Photoshop are left to handle the visual design side of the web, i.e. logos, icons, and illustrations. However, I think that can start to change as well.&lt;/p&gt;
&lt;p&gt;I was first inspired about visual design in the browser while reading an interesting article about creating geometric patterns in Illustrator&lt;sup&gt;&lt;a href=&quot;https://hipsterbrown.com/training-data/Visual-Design-in-Browser/#two&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, and I thought about how much easier this would be in Sketch—then how much easier it would be with HTML &amp;amp; CSS. So I spent some time in CodePen, making some CSS magic with a little help from Sass and Borbon in order recreate the article&#39;s first example without any images. The end result was this:&lt;/p&gt;
&lt;p data-height=&quot;384&quot; data-theme-id=&quot;0&quot; data-slug-hash=&quot;yHafJ&quot; data-user=&quot;HipsterBrown&quot; data-default-tab=&quot;result&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;http://codepen.io/HipsterBrown/pen/yHafJ&quot;&gt;Cubical Pattern Background&lt;/a&gt; by Nick Hehr (&lt;a href=&quot;http://codepen.io/HipsterBrown&quot;&gt;@HipsterBrown&lt;/a&gt;) on &lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen&lt;/a&gt;&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;CSS certainly has come a long way from just changing basic color and size changes in web design.&lt;/p&gt;
&lt;p&gt;That pen got such a nice response from the CodePen community, I decided to finish off the article&#39;s examples with a second recreation in code:&lt;/p&gt;
&lt;p data-height=&quot;384&quot; data-theme-id=&quot;0&quot; data-slug-hash=&quot;qnBja&quot; data-user=&quot;HipsterBrown&quot; data-default-tab=&quot;result&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;http://codepen.io/HipsterBrown/pen/qnBja&quot;&gt;Triangular Pattern Background&lt;/a&gt; by Nick Hehr (&lt;a href=&quot;http://codepen.io/HipsterBrown&quot;&gt;@HipsterBrown&lt;/a&gt;) on &lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen&lt;/a&gt;&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;These pens were so much fun to make, partly because they look so great and partly because they helped me flex the ol&#39; creative muscle when thinking about how to solve this challenge. Nowadays, with the right amount of creative thinking and knowledge, you can create works of art with code. With that thought, I wondered why visual design couldn&#39;t move to the browser as well. As I continued thinking, I realized the numerous advantages of following this visual design practice.&lt;/p&gt;
&lt;p&gt;Visual design in the browser leads to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;~ less http requests, just one solid style sheet&lt;/li&gt;
&lt;li&gt;~ lighter pages &amp;amp; speedy load times&lt;/li&gt;
&lt;li&gt;~ quicker design changes on the fly, i.e. color, size, and layout.&lt;/li&gt;
&lt;li&gt;~ the ability to add additional animated effects for interactive emphasis&lt;/li&gt;
&lt;li&gt;~ always Retina/HD/4K ready assets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This doesn&#39;t replace the visual designer—rather, it challenges the front-end designers to think outside the box in order to implement the visual idea conceptualized originally by the visual designers and illustrators. And of course, not everything can be done with some fancy CSS, but there is a surprising amount of things that can be accomplished. I have even started a Collection of visual design examples&lt;sup&gt;&lt;a href=&quot;https://hipsterbrown.com/training-data/Visual-Design-in-Browser/#three&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; on CodePen to showcase the ability and ingenuity of brilliant front-end folks in that community.&lt;/p&gt;
&lt;p&gt;So the next time you get an image asset of a simple company logo or a set of navigation icons, why not try to go the extra mile and build them with all the power a modern browser provides—then share them with the community through sites like CodePen so we can all learn together.&lt;/p&gt;
&lt;p&gt;My last example shows the simplicity and ease of generating the CodePen logo and icon with some CSS. First, the vector image I did fairly quickly in Sketch &lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/CodePen+Icon%402x.png&quot; alt=&quot;CodePen Icon for iOS 7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Then, the demo because it did happen:&lt;/p&gt;
&lt;p data-height=&quot;416&quot; data-theme-id=&quot;1847&quot; data-slug-hash=&quot;gDbec&quot; data-user=&quot;HipsterBrown&quot; data-default-tab=&quot;result&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;http://codepen.io/HipsterBrown/pen/gDbec&quot;&gt;Sassy CodePen Icon&lt;/a&gt; by Nick Hehr (&lt;a href=&quot;http://codepen.io/HipsterBrown&quot;&gt;@HipsterBrown&lt;/a&gt;) on &lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen&lt;/a&gt;&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;If you do come up with your own awesome examples, feel free to share them with me on Twitter @HipsterBrown or tag them on CodePen as &amp;quot;hip visual design&amp;quot; and I&#39;ll add them to my Collection.&lt;/p&gt;
&lt;h1&gt;References &amp;amp; Resources&lt;/h1&gt;
&lt;p id=&quot;one&quot;&gt;&lt;sup&gt;1&lt;/sup&gt; &lt;a href=&quot;http://webdesign.tutsplus.com/articles/workflow/tips-for-designing-in-the-browser/&quot;&gt;Tips for Designing in the Browser&lt;/a&gt; &lt;/p&gt;
&lt;p id=&quot;two&quot;&gt;&lt;sup&gt;2&lt;/sup&gt; &lt;a href=&quot;http://veerle.duoh.com/design/article/creating_geometric_patterns_in_illustrator&quot;&gt;Veerle Pieters- Creating Geometric Patterns in Illustrator&lt;/a&gt;&lt;/p&gt;
&lt;p id=&quot;three&quot;&gt;&lt;sup&gt;3&lt;/sup&gt; &lt;a href=&quot;http://codepen.io/collection/EjmAI&quot;&gt;A Collection of Visual Design Pens&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen FTW&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://css-tricks.com/examples/ShapesOfCSS/&quot;&gt;CSS Shapes Guide on CSS-Tricks&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Free Is Nice</title>
    <link href="https://hipsterbrown.com/training-data/Free-Is-Nice/"/>
    <updated>Wed, 09 Oct 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Free-Is-Nice/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;There is a considerable amount of free stuff on the Internet: videos, pictures, apps, books, and tons of information. I&#39;ve definitely viewed and downloaded my fair share of content, but I haven&#39;t really given much back in return. So I while procrastinating on a personal project, I came up with a two things people might like to use. So now I&#39;m putting them out on the web for free, along with a brief explanation of the process behind their creation.&lt;/p&gt;
&lt;h2&gt;iOS 7 Icon Grid&lt;/h2&gt;
&lt;p&gt;&amp;lt;img src=&amp;quot;https://s3.amazonaws.com/Hip_Musings/images/iOS7_App_Icon_Grid%402x.png&amp;quot; alt&amp;quot;iOS 7 App Icon Grid&amp;quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Since iOS 7&#39;s announcement and recent release to the public, one of the greatest bits examined by app makers is the new icon style.Not only are there new standards about the use of graients and realistic elements (something Instagram chose to completely ignore but I digress) in iOS 7 but a whole template was provided by Apple for icon designers to use so all the apps on your screen have a perfect visual flow. However, &amp;quot;provided&amp;quot; is a funny word because while Apple has plenty of pictures of this template, but they never released any sort of assets for designers to actually use in the creation process, i.e. Photoshop/Illustrator or Sketch.app templates. That left it up to people like Marc Edwards of Bjango to make &lt;a href=&quot;http://dribbble.com/shots/1248681-Bjango-Actions-2-0-PSDs-Actions&quot;&gt;these assets&lt;/a&gt; for themselves and the community. Marc is part of the majority that still uses Photoshop as their main design tool but Sketch by Bohemian Coding is starting to gain popularity, so it&#39;s about time assets were created for that community as well.&lt;/p&gt;
&lt;p&gt;So I took an afternoon to make my own iOS 7 Icon Grid for myself and the Sketch community, and even practice using it too.&lt;/p&gt;
&lt;p&gt;Prior to this undertaking, I had no experience with iOS icon design and minimal experience with Android design (something left for another story about how many apps are doing this wrong). As with most new projects, I started with a little research and learned about the new dimensions for iOS icons in order created a new artboard in Sketch. Most of the design process was eyeing a picture of the template online, open side-by-side with Sketch, and experimenting with all the different elements of the grid until something cohesive stuck. I finalized the product by grabbing the .png I was viewing, dropping it into Sketch to overlay my grid with it, and tweaking my grid until they matched.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/HipsterApp+Icon+Grid%402x.png&quot; alt=&quot;HipsterBrown Icon for iOS 7 Light&quot; /&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/HipsterApp+Icon+2+Grid%402x.png&quot; alt=&quot;HipsterBrown Icon for iOS 7 Dark&quot; /&gt;
&lt;p&gt;I tested the grid out by making a quick app icon using my personal logo, and tweaked that image until it worked within the grid&#39;s golden ratio.&lt;/p&gt;
&lt;p&gt;Overall, this was an interesting exploration into the area of icon design for Apple&#39;s latest mobile operatng system. The OS is still so very young so some of the older apps feel resistant to the change-Instagram- but the lack of visual flow with the rest of the interface will leave them buried in one of the infinite folders in the depths of users&#39; homescreens.&lt;/p&gt;
&lt;h2&gt;Ink &amp;amp; Paint iPhone Wallpaper&lt;/h2&gt;
&lt;p&gt;The second freebie started out as an idea just for a simple illustration or icon inspired by Disney animators and turned into my first experiment making wallpapers for the iPhone.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Ink%26Paint_Example.png&quot; alt=&quot;Wallpaper Example&quot; /&gt;
&lt;p&gt;The Inking and Painting Building is a vital stop during the tour of Disney&#39;s Animation process. My typographical inspiration came from &lt;a href=&quot;http://www.flickr.com/photos/lorenjavier/5677597621/lightbox/&quot;&gt;the lettering above the doors&lt;/a&gt; to the original buildings at the Walt Disney Studios. For other fans of the Disney creative process, I figured it would be fun to have a similar entryway to their phone.&lt;/p&gt;
&lt;p&gt;Another technology that drew my interest for the project was the ability to create parallax backgrounds for iOS 7. Simply making your wallpapers 200 pixels longer and wider technically made them parallax-able. The real neat part of creating these types of backgrounds is adding elements that have their movements tracked as the user&#39;s phone is tilted in multiple directions. This motivated the idea of placing handmade ink splotches and brush strokes meticulously around the lettering.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Ampersands_For_Days.png&quot; alt=&quot;Ampersands For Days&quot; /&gt;
&lt;p&gt;The only snag I had with creating the custom typeface was the ampersand dividing INK and PAINT. Good golly miss molly, ampersands are quite the challenge for a novice at typographic design. I felt it would either make or break the message as a whole, and I certainly spent a majority of my time tweaking that one character every time I returned to the project. I eventually fell back to Google and found a great article about the &lt;a href=&quot;http://www.webdesignerdepot.com/2010/01/the-history-of-the-ampersand-and-showcase/&quot;&gt;History of Ampersands&lt;/a&gt;-- this is something I never thought I would find interest in a year ago.&lt;/p&gt;
&lt;p&gt;I really enjoyed working on more of a static design for once, as opposed to my usual web work and worrying about the constantly varying size changes. Being able to work with several different design elements helped me find a new cohesiveness in my piece.&lt;/p&gt;
&lt;p&gt;You can download all my freebies below, all I ask for in return is a quick share of the article so others can enjoy this content as well. Please reach out to me on Twitter or email me from my contact page if you have any comments or constructive criticism.&lt;/p&gt;
&lt;p&gt;Thanks for reading and enjoy!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.amazonaws.com/Hip_Musings/images/iOS7+Icon+Grid.zip&quot;&gt;Download iOS 7 Icon Grid&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.amazonaws.com/Hip_Musings/images/Ink%26Paint_Messy_4S%402x.png&quot;&gt;Download 4/4S Wallpaper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3.amazonaws.com/Hip_Musings/images/Ink%26Paint_Messy_5%402x.png&quot;&gt;Download 5/5S Wallpaper&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Future of Tech is In and Near Our Hands</title>
    <link href="https://hipsterbrown.com/training-data/Future-of-Tech/"/>
    <updated>Fri, 27 Sep 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Future-of-Tech/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;It seems like every time I take a long car trip with my girlfriend, we start to discuss the latest in technical innovations and what might be the technology of the future. We each have our own sources of information about what is up and coming in the tech world. She listens to the Science Friday program on NPR and I read whatever interesting articles happen to appear in my Twitter timeline from the various tech sites/people I follow. I obviously have an upper hand when it comes to the latest news, while she gets a more curated form of information. With our combined knowledge, I think we have a good idea of what is to come for the technology we use day to day in the future.&lt;/p&gt;
&lt;p&gt;First, a little primer of what other people are talking about before we take a peek into my imagination. Many of us have seen video&#39;s like &lt;a href=&quot;http://www.microsoft.com/office/vision/&quot;&gt;Microsoft&#39;s Vision of the Future&lt;/a&gt;. This video has such a spectacular display of computer graphics and simulated innovation that it&#39;s hard not to believe that it could one day be real- especially if big companies like Microsoft are dreaming of it, too. I was in that same boat of believing that one day we could video chat halfway across the world with our transparent smartphones powered by a holographic display-- that is until I read a fascinating article by Bret Victor called &lt;a href=&quot;http://worrydream.com/ABriefRantOnTheFutureOfInteractionDesign&quot;&gt;A Brief Rant on The Future of Interaction Design&lt;/a&gt;. Bret presents an interesting point about Microsoft&#39;s vision: he argues that the video shows a stagnant view of the way we already use things, and that it continues to limit the way we use our technology. Why should we continue to learn new and unnatural gestures such as swiping, tapping, or pinching with just one or two of our fingers, when there is so much more that we can do with the rest of our hands that we have been doing for hundreds of years?  Even in the rare cases when we do use our whole hand, like switching applications or screens on an iPad or Mac, we still accomplish just one task.&lt;/p&gt;
&lt;p&gt;There is also a future in the innovation of screens. I don&#39;t mean increasing the resolution until there is an IMAX movie theatre sitting in my pocket. I&#39;m talking about the machines that run the plethora of screens around us all the time. Right now, my two favorite screens are the one on my wrist and the one in my pocket. I backed the &lt;a href=&quot;http://getpebble.com/&quot;&gt;Pebble Smartwatch&lt;/a&gt; since the beginnings of it&#39;s Kickstarter campaign and have been wearing it nearly non-stop since February of this year (and I have the tan-lines to prove it). But after all is said and done, it is a convenience for me to wear a Pebble rather than pull my phone out my pocket every time I get a notification. My iPhone has become more of a necessity as I dive deeper and deeper into an always-online life-- it is the one thing I insist on having with me at all times and it powers some of the most important decisions I make every day. Now enough with the primer, let&#39;s paint a pretty picture of the future.&lt;/p&gt;
&lt;p&gt;--&lt;/p&gt;
&lt;p&gt;Picture this:&lt;/p&gt;
&lt;p&gt;Wake up in the morning feeling like P-Diddy….Kidding, let&#39;s not.&lt;/p&gt;
&lt;p&gt;I wake up to sound of my phone&#39;s morning alarm on my nightstand dock and the vibration of my smart cuff. The tap of my hand on the stand snoozes the alarm because the smartcuff around my forearm is within Bluetooth range. I roll out of bed and flick the air. My bedroom lights fade on, without blinding me. I swing my arms like I&#39;m conducting an invisible orchestra and my morning playlist blasts from the bedroom speakers. My music follows me as I shuffle into the bathroom, and then I grip the air in front of me and turn my wrist clockwise. Water starts to pour of the shower head, and I adjust the heat based on the temperature readings on my cuff&#39;s display.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Alright stop- explanation time.&lt;/p&gt;
&lt;p&gt;There are technologies coming to fruition that allow us to re-imagine how we interact with the technology around us, like the &lt;a href=&quot;https://www.leapmotion.com/&quot;&gt;Leap Motion controller&lt;/a&gt;. Leap is essentially a tiny metal box of sensors that can track your hands and fingers in the air, so developers can program their desktop/web apps to use gestures like grabbing, thumbs up, or writing in the air for increased functionality. Elon Musk of Tesla Motors and SpaceX has a &lt;a href=&quot;http://www.youtube.com/watch?v=xNqs_S-zEBY&quot;&gt;great video&lt;/a&gt; demonstrating how this can be used. Leap is still primarily for developers but, as you can see from that video, there is positive work being done to integrate this device with commercial software. However, this display interaction is not engaging enough because there still isn&#39;t any tactile feedback from the device.&lt;/p&gt;
&lt;p&gt;Thalmic Labs has been working on an amazing gadget to fix the tactile problem called &lt;a href=&quot;https://www.thalmic.com/en/myo/&quot;&gt;Myo&lt;/a&gt;, and the video on the landing page perfectly displays the incredible potential behind it. Myo is a forearm cuff packed with sensors that track the minute movements of your arm muscles and, by literal extension, your hand muscles. This allows for more finely-tuned gestures that motion sensors and cameras just can&#39;t compete with. The Myo could allow for haptic (tactile) feedback through the use of a vibration pack or micro-electric signals that simulate touch.&lt;/p&gt;
&lt;p&gt;Now let&#39;s take a look back at the smartcuff that seemed to make magic happen during my morning routine. This cuff could be the future combination of technologies used by Myo, Pebble, &lt;a href=&quot;http://store.nike.com/us/en_us/pd/fuelband/pid-669575/pgid-670534&quot;&gt;Nike&lt;/a&gt; and &lt;a href=&quot;https://disneyworld.disney.go.com/plan/my-disney-experience/bands-cards/&quot;&gt;Disney&lt;/a&gt; today. As an extension of your phone, the cuff would be connected to it via the latest evolution of Bluetooth, or similar tech, and constantly communicating your movements back to your phone for analysis and review later. Sensors similar to the ones found in the Leap Motion controller could be placed all over a house or building to translate your gestures into the operative task. This is particularly enterprising for the germaphobes who will never have to touch a questionable surface ever again.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Let&#39;s continue with my day:&lt;/p&gt;
&lt;p&gt;On my way into the garage, I quickly brush my cuff against the fridge handle to receive an immediate inventory of its contents and what I may need to pick up at the grocery store later. I plug my phone into the dock compartment between my e-bike&#39;s handlebars and start pedaling to work. Once I get to the office and hang up my bike, I dock my phone to my desk and take a seat. Both of my monitors immediately light up with my desktop home screen- my bluetooth keyboard and trackpad glow briefly to signal they&#39;re active, and I get to work. About an hour later, my phone rings, and I see a caller notification appear on one of my monitors. I mime a phone with my cuffed hand, bring it up to my face, and begin speaking into my pinky to answer the call. Once the call has ended, I hang up my invisible phone and return to work. When it&#39;s time to go home, I pick my phone off the dock, and the monitors go dark.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;So a few things happened in that last part. First, let&#39;s discuss the phone situation:&lt;/p&gt;
&lt;p&gt;Over the years, the hub of the Apple ecosystem has moved from the desktop computer to the mobile device, thanks to iCloud. When iCloud came along it became the wireless hub where all your devices can stay in sync with other cloud storage services that store your settings, notes, photos, etc. As cloud storage continues to increase (you can get over a terabyte with &lt;a href=&quot;http://www.spacemonkey.com/&quot;&gt;some services&lt;/a&gt; !) and the age of the Superphone begins (a &lt;a href=&quot;http://www.apple.com/iphone-5s/&quot;&gt;64-bit smartphone&lt;/a&gt; is a big deal! ), what&#39;s to stop consumers from using just one device across multiple empty screens? While this idea is &lt;a href=&quot;http://www.asus.com/Tablets_Mobile/PadFone/&quot;&gt;nothing new&lt;/a&gt;, the OS and implementation are not quite ready yet. Imagine owning a quad-core 64-bit phone with a couple gigs of RAM that you can dock to multiple screens via a universal connector and run truly responsive applications which give additional functionality for larger screens. This device would be constantly connected to the cloud to back up and store all the information you require and is always-online to communicate with the world of Internet-connected devices around it. With this technology, tablets and laptops would become portable docking stations containing enormous batteries that will charge your phone while in use. Imagine never again worrying about your files not syncing between devices, or mobile apps not having desktop counterparts.&lt;/p&gt;
&lt;p&gt;Now about that phone call. When your phone is connected to another screen, what do you do when you actually have to use it as a phone? That&#39;s where the cuff comes in as the natural extension of the phone&#39;s capabilities. The theory behind using the hand-phone is the same employed by Bluetooth headsets today. The vibrations from your jawbone travel through your hand and up your arm to be accepted by the tiny microphone/speaker on the cuff, which then relays the info to your phone. As the person answers, the process is reversed and you have seamless communication.&lt;/p&gt;
&lt;p&gt;Finally, the first interaction with the fridge. RFID is a small but powerful technology employed by many companies to relay information between devices, much like NFC except it is strictly one-way communication and works at greater distances. The theory behind the fridge&#39;s inventory system is an RFID reader in the door of the appliance that reads tags embedded in the food packaging to take stock of your latest grocery trip. It would be able to register expiration dates and nutritional information as well as take the initial weight from the scales integrated in all of the refrigerator&#39;s shelves and compartments to let you know if you&#39;re running low on milk, or if that milk has expired. Simply tapping the RFID area on the handle of the fridge with an applicable reader like the smartcuff allows instant transfer of information. *This is what we need in our refrigerators, Samsung/LG/GE, not the ability to tweet from the door. Notifications from our kitchen and washroom appliances could aid in saving water, power, and pots and pans forgotten on an active stove.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;On to the final part of a day in the life of future me.&lt;/p&gt;
&lt;p&gt;The work day is done and now it is time to enjoy a night out with some friends. We decide to meet at a local bar. On the way, I pass an ad-board offering 20% off if I like/follow/add their [insert new social media application here] page. Interested in saving a bit of money, I give the board a thumbs up with my cuffed hand and receive an instant notification of the coupon being sent to my email. At the bar, we all order our meals, followed by a couple rounds of drinks. When the time comes to settle our bills, the waitress brings over the e-bill pads, and we all tap our cuffs to the pad&#39;s reader and press our index fingers to the adjacent scanner to confirm the payment. After returning home from a pleasant evening out, I prepare for bed and wonder how we ever could have lived without such convenient technological advances.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;OK, final overview. First things first--the interactive ad board delivering that helpful coupon:&lt;/p&gt;
&lt;p&gt;Engaging and responsive ads are found all over the web, but they don&#39;t have to stop there. Seeing an ad you like doesn&#39;t have to mean taking a picture of the url or ugly QR code to remember it- instead, giving it a natural reaction like a thumbs up will delight users and advertisers alike. The instant feedback will also be enjoyable for the loads of ADHD consumers in the world who can&#39;t stand to wait for a delayed email confirmation.&lt;/p&gt;
&lt;p&gt;The payment process would be a much quicker, more secure, and environmentally friendly solution than our current situation. Instead of handing over cash or a piece of plastic with personal account numbers out in the open and waiting for the staff member to return with it, we can shorten and secure the process by using the protective assets of our smart cuffs/phones and unique fingerprints directly. Fingerprint technology is evolving rapidly to become faster and more secure than ever before- just take a look at the &lt;a href=&quot;http://www.apple.com/iphone-5s/features/&quot;&gt;newest iPhone&lt;/a&gt;. This two-step form of verification would be much harder to replicate and would save a lot of paper by reducing the use of wasteful receipts. Of course there will be speculation of reliability--but there always when such a radical paradigm shift is introduced.&lt;/p&gt;
&lt;p&gt;I hope you enjoyed this little adventure into the (hopefully) not-so-distant future of consumer technology. I don&#39;t hold claim to the originality of any of these ideas, they are merely a collection of today&#39;s technology used in more advanced and convenient ways. Let me know what you think by reaching out to me on Twitter, shooting me an email from my contact page, or commenting below. I look forward to hearing your thoughts on the future as well.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>A Personal Site and Delicious New Blogging Service</title>
    <link href="https://hipsterbrown.com/training-data/Personal-Site-&amp;amp;-Blogging/"/>
    <updated>Sun, 15 Sep 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Personal-Site-&amp;amp;-Blogging/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;I&#39;m always looking for something new to work on in my spare time because I can&#39;t help just not being productive, even after hours of class or work. This new project started out as a way to test out an interesting new blogging platform I had stumbled across called &lt;a href=&quot;http://postach.io/&quot;&gt;Postachio&lt;/a&gt; and the process of creating a personal site/blog around it. But as the project began to take shape, it became more about working on a personal brand and creative site with a blog attached.&lt;/p&gt;
&lt;p&gt;When I said I stumbled upon Postachio, it was more like they had stumbled upon me, and more specifically, my Twitter.
After mentioning to a friend the want to learn about the Evernote API, I suddenly got a new follower called &lt;a href=&quot;https://twitter.com/postachioapp&quot;&gt;@PostachioApp&lt;/a&gt;. Quite curious about what an &amp;quot;Evernote Powered Blogging Community&amp;quot; could possibly be, I took a quick look at their website. From my brief glance, this seemed like a novel, yet simple, idea.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Postachio_Home.png&quot; alt=&quot;Postachio Homepage&quot; /&gt;
&lt;p&gt;Developed for the &lt;a href=&quot;http://blog.evernote.com/blog/2013/08/02/announcing-the-devcup-2013-category-winners/&quot;&gt;Evernote Devcup 2013&lt;/a&gt;, the folks behind Postachio had turned the universal note-keeping app into the perfect tool for drafting and publishing a blog post from nearly any device. I already use Evernote for keeping all my class notes in one place and drafting my Hip Musings, so the ability to publish those musings straight to my blog from my iPhone, iPad, Mac, or any computer from the web, astounded me. In order to test out the practical use of such a service, I really wanted to give Postachio a fresh, honest assessment, not just create a sample blog that I would never use since I had just rebuilt my current one. So it was my girlfriend&#39;s need for a personal website that inspired me to finally give Postachio a shot in a real world situation and create a personal site/brand for someone other than myself.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Moleskin_Sketch.jpg&quot; alt=&quot;Moleskine Sketches&quot; /&gt;
&lt;p&gt;I started out the project as I do with most, in my Moleskine notebook with a couple a sketches.The greatest advantage I had by making this for Leah, my girlfriend, rather than a paying client was the amount of exploratory freedom because my girlfriend had almost no ideas for what she would like in her site, where a client would have some clue in advance for what they want. The inspiration for clouds as the central theme of the site came from a poem Leah wrote two years ago, which can be read by clicking the tagline below the clouds. I wanted to make the site a light, airy space for people to visit and enjoy just staring at it for a while, as well as a unique environment for visitors to learn more about her. The original idea for her personal branding was &amp;quot;Just Leah&amp;quot;, honoring one of her favorite books called Just Ella, which also reflected her enjoyment of the Cinderella story with a modern twist. But she found the name too forcefully humble, and we ended up brainstorming a few more ideas until we thought of &amp;quot;Finding Leah.&amp;quot; The brand can be used in various ways if she decides to expand the site and matches the cloud links of Seeker (résumé), Storyteller (writing samples), and Time-Traveler (book review blog) for visitors to find out more about Leah. I strived to make it interesting but not childish and capture the creativity that a writer&#39;s website should have.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Sketch.png&quot; alt=&quot;Site Mock-Up&quot; /&gt;
&lt;p&gt;From there I switched over to Sketch and started creating a quick mock-up of the site. I still enjoy making high-fidelity mock-ups to play around with colors, typefaces, and layouts much easier than going straight to the browser. I planned to use a stoic sans-serif for most of the display text on the site and a playful script font for her name. I eventually found Lavanderia from &lt;a href=&quot;http://www.losttype.com/font/?name=lavanderia&quot;&gt;Lost Type&lt;/a&gt; and Dense by &lt;a href=&quot;http://www.behance.net/gallery/Dense-typeface/10231891&quot;&gt;Charles Daoud&lt;/a&gt;. Dense seemed to fit everywhere except for the tagline, Head in the Clouds, so I ended up creating a lighter version using Sketch&#39;s vector text tool. I chose the blue background as it was Leah&#39;s favorite color and it went great as a pseudo sky for the clouds to cover. After the mock-up was approved, I exported my image assets and started work in Sublime Text.&lt;/p&gt;
&lt;p&gt;This project was also an excuse for me to get more practice using Git and GitHub for version control. So as I set up my development environment with git, Sass, and Bourbon, I also set up a new public repository on GitHub to keep a back up of my changes. You can find all the source code &lt;a href=&quot;https://github.com/HipsterBrown/postachio_project&quot;&gt;here&lt;/a&gt;. I quickly connected local project with the GitHub repo and began creating the site layout.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Finding_Leah.png&quot; alt=&quot;Finding Leah&quot; /&gt;
&lt;p&gt;As soon as the layout and styling matched the mock-up, I began implementing the more interactive elements that would make this site more appealing for its visitors. My initial idea was to give the clouds some life with some continual CSS3 animations. I tested out several different animation times, directions, and patterns until I got the alternating clouds just right so they were not distracting but still enticing. Eventually it came down to creating pages for the résumé and writing samples, which was one of the more difficult decisions of the project.&lt;/p&gt;
&lt;p&gt;I could have easily made additional pages for the résumé and sample content to sit on and the clouds linked to externally, but that seemed so impersonal and dragged the user away for the main page. I had never created any sort of modal window for any of my past projects, but I figured this was the best time to try and implement one or two. Modals can be annoying for users just trying to complete a simple task and often times take longer than their worth to load in content. So I took a new approach to this old idea by having all the content loaded with the page, rather than loading it in later after the user clicked on the matching link, and created custom modals to bring that content into view when needed. Since it was just a small amount of information to add to the original page, it was worth the sacrifice of milliseconds during the initial load.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Modal_Samples.png&quot; alt=&quot;Writing Sample Modal&quot; style=&quot;width: 500px; margin: 0 auto;&quot; /&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Modal_Resume.png&quot; alt=&quot;Résumé Modal&quot; /&gt;
&lt;p&gt;The layout of each modal was inspired by Google&#39;s well-known card style seen in Google Now and spreading to its various other services. I really enjoy the easy separation of unrelated content by using these cards to differentiate writing samples and résumé categories. It was also a clean and modern look that matched the blog and main page of the site. I added the download links for the writing samples and résumé so that potential employers could keep a record of their interest in Leah. The buttons were the only things not custom designed by me; instead I used Bourbon&#39;s great, built-in button mixin for Sass. I decided to design the modals in the browser because of the simplicity behind the design.&lt;/p&gt;
&lt;p&gt;Finally, it came down to working with Postachio and creating the custom book review blog for Leah to use.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Postachio_Details.png&quot; alt=&quot;Site Form&quot; /&gt;
&lt;p&gt;Signing up and getting a default blog up and running is fairly straight forward; just authorize Postachio with your Evernote account and you can start arranging your blog details within a minute.
The blog creation form is short and sweet:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pick a subdomain to host your site&lt;/li&gt;
&lt;li&gt;Give it a name&lt;/li&gt;
&lt;li&gt;Give it the blog author&#39;s name&lt;/li&gt;
&lt;li&gt;Decide which Evernote notebook to sync all the articles with or create a new one&lt;/li&gt;
&lt;li&gt;Choose what social media links to include&lt;/li&gt;
&lt;li&gt;Hook up your disqus commenting&lt;/li&gt;
&lt;li&gt;Decide whether or not to use Markdown
I personally love using Markdown in my current blogging environment, Anchor CMS, and it is awesome for Postachio to give users more control of their formatting by including it.&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Postachio_Themes.png&quot; alt=&quot;Theme Browser&quot; /&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Postachio_Source.png&quot; alt=&quot;Source Code&quot; /&gt;
&lt;p&gt;Styling your Postachio blog can go one of two ways; you can either choose one of the pre-built themes or style it by hand by diving into the source code. I obviously chose the source code route for further control and soon discovered that the default theme is built with the Twitter Bootstrap framework and the Liquid layout language from the folks at Shopify. Twitter Bootstrap is a popular framework for developers to get their projects looking pretty with minimal effort during the prototyping stage, but the numerous naming conventions and fairly bulky size make it a little too much for running small blog, especially since Postchio is hosting all the accounts themselves. Targeting any of the blog elements for styling meant going into the Web Inspector and checking to see exactly what the developers called it. Once you got past that issue, you could be assured that the blog was mobile friendly because Bootstrap was in use. I did not see the use in changing too much of the format, it was mostly color choices, but it was probably more than the average user would want to try and figure out. It felt a lot like the old MySpace days when you found bits of code to paste into your site&#39;s customizing area and hoping it would work the way you wanted it.&lt;/p&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Blog.png&quot; alt=&quot;Leah&#39;s Blog&quot; /&gt;
&lt;p&gt;My final thoughts for Postachio would be that it is a brilliant concept that just needs a little more work to make it one of the most popular tools for blogging. Lightening the browser load by dropping Bootstrap for a custom default framework would be beneficial in the future, not just for their users but their servers as well. I would highly suggest this tool over anything like Wordpress, which has become far too bloated for setting up a simple blog, but I do not yet see it as a tool for creating an entire website. At the moment users can set up multiple pages for free but the creators are planning on making that a premium feature later on. I think it is smart for them to be thinking in line with Evernote&#39;s pricing strategy, give a nice amount of features away for free and then create some really awesome features for the users willing to pay for them.&lt;/p&gt;
&lt;p&gt;Ultimately, I felt the site was a success from start to finish. It gave me a fantastic new experience of working with a client to create a personal site that would interest potential employers and give her something she is proud to put on her future job applications. I will continue to maintain the latest writing samples and résumé for the site until I find a better way for Leah to implement them herself. Hopefully she will enjoy the use of Evernote to draft her book reviews and publish them to her blog from wherever she likes. Only time will tell but from my brief experience, I think it&#39;s the perfect tool for creating a custom, lightweight blog for clients to post to with ease. You can see the live project &lt;a href=&quot;http://hipsterbrown.com/postachio/index.html&quot;&gt;here&lt;/a&gt; and Leah&#39;s live site at &lt;a href=&quot;http://leahmarks.com/&quot;&gt;LeahMarks.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now it is time for me to get on to my next project, whatever that may be, and keep my eyes out for some more interesting tools to test.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The 3 L&#39;s of an Enthusiastic College Graduate</title>
    <link href="https://hipsterbrown.com/training-data/The-3-Ls/"/>
    <updated>Thu, 05 Sep 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/The-3-Ls/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Location, location, location&lt;/p&gt;
&lt;p&gt;With two semesters, aka 8 months, left before I complete my undergraduate curriculum and enter the scary, and very real, world of independent adulthood, so I&#39;ve been thinking a lot about my post-graduation plans. As I scroll through the online job boards, &lt;a href=&quot;http://jobs.37signals.com/&quot;&gt;37Signals&lt;/a&gt;, &lt;a href=&quot;https://teamtreehouse.com/jobs&quot;&gt;Treehouse&lt;/a&gt;, and &lt;a href=&quot;http://www.authenticjobs.com/&quot;&gt;AuthenticJobs&lt;/a&gt; (my personal favorite), the biggest factor I take in from each posting is location. Sure the various perks offered by the more generous companies are nice to look at, and as a soon-to-be graduate in this day and age, I should just be grateful to receive employment from whomever is providing a steady paycheck and desk. However, as an ambitious student, I still believe my first job experience will be majorly impacted by where I&#39;ll be living.&lt;/p&gt;
&lt;p&gt;A lot of my fellow graduates will try to look within their own state or an hour from where they graduated, thinking it&#39;s too risky to look much further than that. But that is exactly why they should branch out and look across the country or the world for the best opportunity. Our early 20s are really the only time we can easily pack up a couple bags of clothes and our computer in order to just move somewhere else. The majority of students who were detained to going to college in-state when they dreamed of studying abroad now have the freedom to take a chance and explore all of their options.&lt;/p&gt;
&lt;p&gt;The city we decide to work in is not just that; it is where we make our friends, find our hangouts and hobbies, and start to figure out what we really want out of life. In Orlando, I have nearly exhausted all chances to discover anything new in the area. I have attended the downtown meetups, visited Universal and Disney so many times I&#39;ve memorized the dialogue on The Mummy and Jungle Cruise, and even explored the surrounding wilderness while &lt;a href=&quot;http://www.geocaching.com/&quot;&gt;geocaching&lt;/a&gt;. There are still a few rocks left unturned but not nearly enough for me to stick around another 4 or 5 years.&lt;/p&gt;
&lt;p&gt;I am fortunate enough to have a skill set that is universal to almost any region of the country and not restricted to just one industry. As I started to discover the incredible need for designers and front-end developers, I decided I would like to travel following my departure from higher-education. The bulk of my efforts are looking out of the country, due to the fact that I have never left United States outside a three day cruise to the Bahamas at the age of 10. Why visit Europe for a short and expensive stay, when I can instead be paid to live there and explore as much as I would like during my free time. The main arguments I&#39;ve heard against this concern the paperwork required, the small flats I&#39;ll have to live in, and the lack of automobile accessibility, but these are paltry obstacles in the way of biking through the English countryside, watching the Eiffel Tower light up at night, and taking a boat down the Danube because they are all just a train ride away.&lt;/p&gt;
&lt;p&gt;Side Note:
As to my family members who may be reading this and wondering why I&#39;m not too concerned about being so far from home during the holidays and birthdays, the Internet is a wonderful thing for instant communication; they even have this crazy thing called Skype and FaceTime for video calling! Plus, who wouldn&#39;t want to visit their son/nephew/cousin in Europe for Christmas or New Years? The five years or so will fly by in the grand scheme of things, and I&#39;ll return with plenty of souvenirs for everyone.&lt;/p&gt;
&lt;p&gt;All that I want out of the city in which I live out my 20s is great public transportation, the ability to bike on real roads rather than highways, and cool places to see/things to do during my free time. Using these requirements, I have whittled down the list to San Francisco, New York City/New England area, and London/outlying area. These are not places where I plan on laying down my baggage for the rest of my life but they are certainly spectacular pit stops as I explore what life has to offer an enthusiastic, self-starting UI/front-end designer.&lt;/p&gt;
&lt;p&gt;So I will continue my cross-country(ies) search for the best opportunity for me, and I have 8 months to complete my task with living/traveling arrangements ready to go. It is quite an undertaking while I&#39;m still finishing up a full class load and job, but it will be completely worth it once I&#39;m boarding the plane and headed off to a new experience. I draw inspiration from my favorite Pixar movie, Up, and remember that, &amp;quot;Adventure is out there!&amp;quot;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Where are the Chief Creative Officers</title>
    <link href="https://hipsterbrown.com/training-data/Chief-Creative-Officers/"/>
    <updated>Tue, 27 Aug 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Chief-Creative-Officers/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;In a world of CEOs, CFOs, and, even, CTOs, that does it take to accept the role of CCO for a startup or business? Is there even room on the top floor for another chief power?&lt;/p&gt;
&lt;p&gt;Ever since the beginning of this technological renaissance, the importance of design has been accepted throughout the web and mobile industries. There are existing upper management roles for creatives at hardware, software, and consulting shops, both big and small; however, these positions are usually labeled as Creative Directors, Product Managers, or Senior Designers. These titles give little weight when set next to Executives, Vice Presidents, and Presidents in a company.&lt;/p&gt;
&lt;p&gt;Let&#39;s break down roles of current chief positions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The CEO is usually the face of a company, making the greatest decisions for the general direction of the company at large, and maintaining the ability to veto the decisions of other officers.&lt;/li&gt;
&lt;li&gt;The CFO is the grand treasurer in charge of general flow of money through a company and keep the monetary decisions in line.&lt;/li&gt;
&lt;li&gt;The relatively new position of CTO is primarily responsible for all the technical details required to run the company, as well as keep the dev team happy and making sure all the executive decisions are implanted correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, doesn&#39;t it make sense to add someone to this team who is in charge of the major design decisions of the company? This person can work with the CTO in order to ensure the technical implementations of the company&#39;s products come out usable, enticing, and interesting when they reach the user. The partnership between CCO and CFO can provide a cost-effective organization of designers and making sure the creative department is set-up with the resources needed to maintain productivity. Finally, the CCO is a direct line of communication from the creative to the CEO and a guide for overall design direction as a company.&lt;/p&gt;
&lt;p&gt;I do believe in an open environment where any member of a company should feel comfortable contacting anyone in the company for the betterment of the company overall. However, bigger companies with hundreds of employees like Google, Apple, or Microsoft, and specific departments for their various skillets or products, then every email cannot always be read by the one Chief Officer.&lt;/p&gt;
&lt;p&gt;The best example of a CCO position in the world today is actually at Apple with Sir Jony Ive as Apple&#39;s Senior Vice President of Design. Even this is a convoluted title for the amount responsibility that lies with Sir Ive in the day to day operations of this very design-oriented company. Where is the President of Design or are those duties passed on to the CEO, Tim Cook? Apple has broken down the leadership of it&#39;s various divisions fairly well, but, as the public face of their hardware (and now software) design, Sir Jony Ive has earned more of a Chief title than almost anyone in the industry today.&lt;/p&gt;
&lt;p&gt;Should ambitious designers be forced to go to business school in order to join the executive ranks and make greater change within a company? Or can the common executives evolve to include a person, like Sir Ive, to keep the importance of design known throughout a company?&lt;/p&gt;
&lt;p&gt;Only time will tell.&lt;/p&gt;
&lt;p&gt;P.S. - I was first inspired to think about this topic after reading about being a &lt;a href=&quot;http://www.waynegreenwood.com/unicorn-shmunicorn-be-a-pegasus/&quot;&gt;daring design pegasus rather than a combo design/dev unicorn&lt;/a&gt;. Let me know what you think on &lt;a href=&quot;https://twitter.com/hipsterbrown&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>An Early Easter Egg Hunt</title>
    <link href="https://hipsterbrown.com/training-data/An-Early-Easter-Egg-Hunt/"/>
    <updated>Fri, 23 Aug 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/An-Early-Easter-Egg-Hunt/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;One of the reasons I love Pixar and older Disney movies is the little Easter Egg, or hidden items, the creators place around their films. In most Pixar films you can find a clue to their next production, A113 in homage to a classroom at CalArts, the Luxo Ball, the easily-recognizable voice of John Ratzenberger, and, of course, the Pizza Planet truck. For the avid fans, these little items make the movie special and challenges the animators to place them inconspicuously throughout each film (except for John; there&#39;s nothing inconspicuous about that voice).&lt;/p&gt;
&lt;p&gt;Many great site and app creators like to take part in this fun as well by hiding neat keyboard tricks, special pages, or references in the mix of their products. If you&#39;re a fan of the popular iOS/Mac Twitter client, Tweetbot, then you remember the &lt;a href=&quot;http://www.cultofmac.com/175487/how-to-access-tweetbots-super-secret-settings-menu-activate-streaming-over-3g/&quot;&gt;super secret settings page&lt;/a&gt; located in the iOS app a few versions back. Things like this enhance the user interest and provide a better appreciation for work done by the developer/designer/whomever.&lt;/p&gt;
&lt;p&gt;So, after struck with inspiration for a fun addition to my site, I created my own little Easter Egg page. It is live now and just waiting for you to find it. If you need a clue or just have some feedback, feel free to mention me on Twitter (it&#39;s the little bird icon at the bottom) or shoot me an email from the contact page.&lt;/p&gt;
&lt;p&gt;Happy Hunting!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Summer Review &amp; Plans for Fall</title>
    <link href="https://hipsterbrown.com/training-data/Summer-Review/"/>
    <updated>Thu, 01 Aug 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Summer-Review/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;August has now reared it&#39;s ugly head, which means end of summer is near and time to prepare for a full class schedule once again. For this first of the August posts, I thought I would review some site stats from the summer and talk about the plans for Fall productivity.&lt;/p&gt;
&lt;p&gt;Since May 1st, which around when I finally went live with the new site, I have had 216 visits to HipsterBrown.com; of that number, I have had 116 unique visitors and 610 page views. For a blogging newbie and student, an average of 6 page views a day is a nice success. My hope is to iterate on some of the design and improve some of the content in order to increase that average during the coming months. I may even end up rebuilding the blog with a different platform, depending on the progress of &lt;a href=&quot;http://tryghost.org/&quot;&gt;Ghost&lt;/a&gt; (yet another Kickstarter project I backed this summer).&lt;/p&gt;
&lt;p&gt;As far as summer productivity is concerned, I vastly improved upon some of my design abilities including illustration and interface creation in Sketch. Before, I considered myself a simple layout and UI designer but now I&#39;m more confident in my ability to create simple illustration and product mockups in my favorite graphic editor. I finished the HTML/CSS portion of my current personal web app project with hopes to complete the dev work this month, which leads to the primary reason for this post.&lt;/p&gt;
&lt;p&gt;The original title to this article was &amp;quot;Dev-dication&amp;quot; but that was trying a bit hard for a catchy, albeit accurate, post title. After all the design work and improvement I made this past summer, I have gotten out of touch with my front-end experience and the ability to build the products I&#39;m designing. So starting in this month, I am dedicating the majority of my work time towards improving my development skills, especially with rapid prototyping and pure Javascript. I already have a few ideas lined up for accomplishing this goal.&lt;/p&gt;
&lt;p&gt;My design work will not be halted because I still have several plans in the area, but it will be the treat for after my dev work is done. I know the basics of JavaScript development, i.e. syntax, events, and DOM manipulation, but I have yet to move past the title of beginner due to my lack of practical experience. All the Codecademy, Treehouse, and Code School course won&#39;t mean jack until I finally put it to good use in the real world wide web. I plan to take the path of JavaScriptIsSexy article &lt;a href=&quot;http://javascriptissexy.com/how-to-learn-javascript-properly/&quot;&gt;How to Learn JavaScript Properly&lt;/a&gt; and start out by building a simple quiz web app. This will improve upon my code organization, DOM manipulation, and form validation abilities right away, and it&#39;s a project that can be updated by using an MVC framework like Backbone or Ember and a templating engine like Underscore or Handlebars. It is certainly a step in the right direction towards proper dev improvement.&lt;/p&gt;
&lt;p&gt;As a student with 9 months left to build up a proper portfolio and obtain full-time employment before being released into the real world, I feel underprepared but ready for what comes next. There is a lot of opportunity out there, as my constant trolling of the various job boards suggests, but it&#39;s hard to anticipate how I am reflected with my current collection of personal projects. While currently classifying myself as a UI/UX/Product designer with some front-end experience is swell for the current job market, being able to confidently call myself a Designer and Front-End Developer creates a whole new realm of flexibility in my job search.&lt;/p&gt;
&lt;p&gt;I hope to share some awesome projects in the next few months, both in design and development, and turn a few more heads towards the site. It all leads towards future improvement and a career in this great industry I have come to know the past few years. I cannot wait to look back at this post in a few months and smile proudly at the work I&#39;ve done.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>My Pixar Theory</title>
    <link href="https://hipsterbrown.com/training-data/My-Pixar-Theory/"/>
    <updated>Sat, 27 Jul 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/My-Pixar-Theory/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Due to the lack of Internet access in my current living situation, I have been going through every Pixar movie in order from the original Toy Story to the end of the great trilogy and watching them with their accompanying audio/visual commentary. For me, the commentaries by the directors, producers, and animators behind these revolutionary films made them even more heartfelt and magical to watch again and again. It only took 11 movies for me to notice a few lessons stowed away in these commentaries that could be used in other industries. More specifically, I want to talk about using these Pixarian principles in product design.&lt;/p&gt;
&lt;p&gt;The first step for almost all Pixar movies is also the last step, figuring out the punchline before writing the joke. For the folks at Pixar this means finding the ultimate lesson that drives the story before starting any basic animation. In product design, this is figuring out what the primary use is for the app, website, or device before any wireframes or sketches are made. As long as the designer stays focused on what the layout, color scheme, and functionality are all working towards, it is a great giant leap towards creating a useful product. For example, if you were designing an iOS mail app called the Pixarian Postal Service then the final goal would be for the user to get their emails in a timely and readable format. No matter what additional features and flare you decide to add later, this should be the focus of any major decision made later to the design.&lt;/p&gt;
&lt;div&gt;
&lt;img src=&quot;https://s3.amazonaws.com/Hip_Musings/images/Pixarian+Postal+Service%402x.png&quot; /&gt;
&lt;/div&gt;
&lt;p&gt;Another key principle to the Pixar filmmaking formula is the ability to take something easily recognizable to the viewer and show a new take on it. Like vehicles and toys coming alive and having fantastical personalities or the monsters in our closets scaring children to power their cities. A bit of fresh perspective is perfect for delighting audiences and keeping their attention. In product design that means taking your product&#39;s main function and rethinking how it should be done to delight and interest the user. For the Pixarian Postal Service, this idea could be implemented by color coding mail based on a list of friends, family, business associates, subscriptions, etc to give the user the ability to quickly scan through their unread mail. Then making these colors customizable and easy to implement is just icing on the cake. It is literally giving the user a different perspective for their email experience.&lt;/p&gt;
&lt;p&gt;Pixar is so great at making their characters seem so life-like and realistic, not just by the detail in the drawing but every single movement being so close to something done in reality. A great lesson to be learned from the animators of Pixar&#39;s many famous films is making a character act relatable by using personal experience as inspiration. The universally applicable version of that statement is using personal experience to create a personable experience for the user/viewer. A product designer and their team should draw inspiration for their designs from their experience using other products like it, which is usually what inspires them to make a better one. When coming up with features, layout, and functionality for the Pixarian Postal Service app, you should remember the gripes and groans you make whenever you had to use the iOS Mail app, Google&#39;s GMail for iOS app, or even Sparrow. Keeping that in mind should lead you to focus on having push notifications at launch and enough available space on your servers so the rush of initial downloads don&#39;t crash them. Also, if you don&#39;t see yourself using the feature then why ask your users to do the same?&lt;/p&gt;
&lt;p&gt;Now probably my favorite part about the Disney/Pixar franchise is their ability to mix fantasy with mundane realities. For example during The Incredibles, the family has just landed an RV on the freeway after falling from a speeding jet and yet, Mr. Incredible and his wife are still arguing about which exit they should take to intercept the giant robot reeking havoc in the downtown area. A fantastical family of superheroes is going to save the day and they still argue about driving direction because they are still a normal family at the core. Product designers shouldn&#39;t be afraid to add a little pixie dust to their designs, as another way to delight their users. In our mobile mail app, this could be adding some nice sound bytes when a user checks their mail and a new letter has just rolled in, like &amp;quot;There&#39;s some mail in my box!&amp;quot; (to the tune of &amp;quot;There&#39;s a snake in my boot!&amp;quot;) and if you can get Tom Hanks to do the voice, that&#39;s even better. If a user didn&#39;t care for this bit of sparkle in the app, which is highly unlikely, then it should also be easy enough to turn off.&lt;/p&gt;
&lt;p&gt;Finally, Pixar has been able to astound so many audiences of all ages by their stories and characters and animation because of something they probably learned from one of their founders, Steve Jobs. He was famous for his attention to detail in hardware and software, making sure even the circuit boards were clean and precise when a majority of customers would probably never see it. This is reflected in all of Pixar&#39;s films, you just have to pay close attention to the backgrounds in each movie. The drawings in Andy&#39;s bedroom during the Toy Story trilogy are usually done by some of the staff&#39;s kids who are the same age as Andy and other animated children during those movies to give it authenticity, down to Andy&#39;s name signed on the bottom of his toys. Every single piece of memorabilia in Mr. Incredible&#39;s home office and all of Carl and Ellie&#39;s knick knacks in their flying house were designed with care by the amazing team at Pixar. One of the directors calls this - &amp;quot;sanding the underside of the drawers&amp;quot;.  As a product designer, it can be easy to save the boring stuff for last and not put as much effort into it; sometimes that means an ugly settings menu or a confusing FAQ section. For the Pixarian Postal Service, you would want your users to love every bit of the design even if they never venture into some of the more in-depth parts of it. Eventually someone will find these shady and dirty bits of the app, then there is no going back.&lt;/p&gt;
&lt;p&gt;Those principles may not be all that Pixar has the offer the world of design but it&#39;s a start. They make movies that will generations because they continue to follow these commandments with every new project, and it makes me more excited each time a new movie is announced to see how they implement them once again. I highly suggest going through a few of the commentaries yourself, especially the Toy Story ones, and you may pick up a few things I may have missed. Now get up and out there to take your designs to infinity and beyond. (Couldn&#39;t help myself, I love puns!)&lt;/p&gt;
&lt;p&gt;P.S. The commentaries tend to point out a few Easter eggs the Pixar crew puts in every movie, like A113, the Luxo ball, a clue to their next movie. If you&#39;re feeling adventurous in your designs, trying adding a few of your own to catch some of the more interested users by surprise. The folks at &lt;a href=&quot;http://tapbots.com/&quot;&gt;Tapbots&lt;/a&gt; are particularly good at this.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Stop Practicing, Start Performing</title>
    <link href="https://hipsterbrown.com/training-data/Stop-Practicing-Start-Performing/"/>
    <updated>Sat, 13 Jul 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Stop-Practicing-Start-Performing/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;As a full-time student with a part-time job and various social responsibilities, like local meetups, dates, and Disney, I can only manage small spots of free time to work on my design and front-end development experience. However it hit me as I was finishing up yet another one of my weekly exams, I am spending way too much time practicing and not enough performing!&lt;/p&gt;
&lt;p&gt;At the beginning of the summer, I came up with several projects to complete by the time my full-time class schedule started again, but I found myself getting distracted, lazy, and forgetful of those once-exciting aspirations. When I finally got around to doing any work, I would feel unprepared and escape back to the online tutorials or library of ebooks I bought to become a better designer/developer. I would keep telling myself that if I just practice a little more then I&#39;ll be ready to conquer [insert project challenge here] and be awesome! Except I would just stay in my cave of guided readings and Codecademy lessons, always putting off the moment when I would actually apply my knowledge.&lt;/p&gt;
&lt;br /&gt;
##&quot;Practice makes perfect&quot; - but what do you have to show for it at the end of the day. Why not put that work to good use and make something useful?
&lt;br /&gt;
&lt;p&gt;This is not a new concept by any means, but it is one that needs reinforcement among the aspiring makers and creators of the web. Whatever you make doesn&#39;t have to be used by millions or even thousands, just as long as it&#39;s something you are proud to put out in the world. Learning by real world experience is one of the easiest ways to conquer that development concept or design technique. Don&#39;t just create some practice site or app and squander it away where no one else can ever see it- you never know what might become of that seemingly insignificant site idea or design.&lt;/p&gt;
&lt;p&gt;This article is meant to be a short and sweet reminder of the dedication required to succeed. After realizing my mistake, I immediately set a schedule and deadline to complete at least one project by the end of the summer. I hope to be back in a couple weeks with a finished product that I will be proud to let others explore and use. I would love to see more people set on this path as well. So, if there is a completed project you would like to show off, please feel free to share it with me on Twitter (just click the bird in the footer) and good luck!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Open Source for Everyone</title>
    <link href="https://hipsterbrown.com/training-data/Open-Source-For-Everyone/"/>
    <updated>Tue, 09 Jul 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Open-Source-For-Everyone/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;This summer has sparked inspiration to not just create new sites, tools, and designs but also give back to the community that has taught me so much. As a designer and front-end developer, I thought the best way for me to contribute was solely through open source projects online. This led to my realization that I have been contributing to open source projects for quite a while now, and I believe others should really give it a try.&lt;/p&gt;
&lt;p&gt;The open source movement has been around since the Internet was a fledgling phenomenon and grown exponentially over the past few years as new tools were released, which made working with open source projects easier for the common computer user. As these tools progressed, the definition of an open source project has also changed to stand for so much more than code.&lt;/p&gt;
&lt;p&gt;The initial open source philosophy started with software developers in the 90s; it stood for releasing free licenses to developing software or code blueprints for other developers to hack in their local environment. The most notable open source project during this beginning period was Netscape&#39;s release of the source code for their Navigator Internet browser in 1998 (there is even a full documentary called &lt;a href=&quot;http://www.imdb.com/title/tt0499004/?ref_=fn_al_tt_1&quot;&gt;Code Rush&lt;/a&gt; about their incredible journey to the final release of the project all on &lt;a href=&quot;http://www.youtube.com/watch?v=a-49a_CjH0M&quot;&gt;YouTube&lt;/a&gt; ). This led to the founding of the &lt;a href=&quot;http://opensource.org/&quot;&gt;Open Source Initiative&lt;/a&gt; to further promote the open sourcing of more major projects from big tech companies, like Mozilla, WikiMedia, and The Linux Foundation.&lt;/p&gt;
&lt;p&gt;The philosophy was popularized in the 90s so sharing the code was stuck to passing around floppy disks since the Internet speeds were far too slow to be discovered over dial-up; it would take a few years until broadband Internet to enable easier distribution. Then came the creation of an enormous stepping stone in the open source community, &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt;. This is a company and web app powered by the motto: Social Coding (for all), and truly believes that great code and projects can be created by a global community of developers, designers, and enthusiasts alike. After hearing a few of the GitHub employees, &lt;a href=&quot;http://joelglovier.com/&quot;&gt;Joel Glovier&lt;/a&gt; and &lt;a href=&quot;http://www.madebygraham.com/&quot;&gt;Matt Graham&lt;/a&gt;, speak about the attitude surrounding the company, I first began to realize the idea behind open sourcing is applied to more areas than just code.&lt;/p&gt;
&lt;p&gt;Before I explored the wonderful world of repositories on GitHub, my first contribution to an open source project was on &lt;a href=&quot;http://www.kickstarter.com/&quot;&gt;Kickstarter&lt;/a&gt;. An awesome smart watch called &lt;a href=&quot;http://getpebble.com/&quot;&gt;Pebble&lt;/a&gt; had just surpassed its initial funding goal in less than a day, and it was something I finally found worthy of investing my money towards. However, I wasn&#39;t really investing in Pebble, nor was I pre-ordering the product months in advance; I was contributing my money and trust to a small group of inventive guys from Canada hoping to develop their product for the masses. Sites like Kickstarter and Indiegogo help individuals and groups share their project with other people around the world and ask for funds to turn their products, software, music albums, plays, movies, and services into a reality. This is labeled as crowdfunding but it can easily be defined as open sourcing product &amp;amp; creative development.&lt;/p&gt;
&lt;p&gt;It is spectacular to see a platform for people to share their dreams and ask the world for a little help getting started. Then backers get to share in those dreams as the project creators continue to share their progress, whether it be good or bad, and get feedback on every step of the way. I would not have believed the amount of work that went into designing, manufacturing, and shipping 80,000 fantastic, little smart watches until I committed to Pebble. I continue to follow along with every update even after receiving my physical reward because I am now part of a community that cares about this company and their idea of wearable technology.&lt;/p&gt;
&lt;p&gt;//Be warned though, crowdfunding can be a tad addicting, at least it was for me. Since Pebble, I have backed nearly 30 projects on Kickstarter. These ranged from major product design to short books, even some independent films and local musicians. Most of these projects succeeded but some have failed, either in meeting their money goal or providing rewards to  their backers. So before you go on a frenzy funding spree, be sure to do a little research on who you&#39;re backing and ask a few questions to ensure your money is being put to good use. //&lt;/p&gt;
&lt;p&gt;Now, money and technological prowess are not the only ways to contribute to a public project; knowledge is a powerful tool as well. There are sites like Wikipedia that have huge collections of open source knowledge on nearly every subject in the human domain. Anyone can contribute and add knowledge to a data base accessible by BILLIONS of people. Forums are another great way to open source questions so that anyone with similar inquiries can immediately find those answers with a quick bit of Googling (doesn&#39;t that sound a little dirty?). Next time you&#39;re scrolling through the [your interest here] forums or exploring the depths of the Wiki world, try adding a thought or tidbit of info. You may be surprised to find how much you like it.&lt;/p&gt;
&lt;p&gt;Any situation in which a problem can be solved or project completed by asking for the help of the community, whether it be global or local, can be defined as an open source achievement. With so many open source options available, anyone with internet access can contribute. Take a survey of your skills and some of the problems that could be solved by the hands of many, and give open source a shot.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Conference Appeal</title>
    <link href="https://hipsterbrown.com/training-data/The-Conference-Appeal/"/>
    <updated>Tue, 25 Jun 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/The-Conference-Appeal/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;This past weekend I attended one of the most life changing events in my career so far; that was Front-End Conf, and this is my reflection from and recommendation for attending. &lt;br /&gt;&lt;/p&gt;
&lt;h2&gt;Reflection&lt;/h2&gt;
&lt;h3&gt;Preparation&lt;/h3&gt;
&lt;p&gt;Front-End Conf came out of nowhere for me. I had heard about the major conferences that visited Orlando and the surrounding area, but it was probably my recent interest in the meetups at Envy Labs or sheer dumb luck that I stumbled across this great little conference before it sold out. It has been around for 5 years now, and more information on it&#39;s inception can be found &lt;a href=&quot;http://the-pastry-box-project.net/dan-denney/2013-june-16/&quot;&gt;here&lt;/a&gt;. I was also fortunate enough to receive a student pricing, which ultimately convinced me that this would be a perfect conference to attend.&lt;/p&gt;
&lt;p&gt;I registered for Front-End Conf when it was first announced, about two months before the actual event, so I had of time to twiddle my thumbs and get even more excited for what I would experience come June 21st. During those two months, I basically kept up-to-date on who was speaking about what, making plans about where to stay, and rebuilding my personal site and designing new business cards for connecting with my fellow designers and developers attending FEC.&lt;/p&gt;
&lt;h3&gt;Day 1&lt;/h3&gt;
&lt;p&gt;On arrival, check-in was an easy and simple process complete with a &lt;a href=&quot;http://www.flickr.com/photos/charliebrwn1209/9138324780/in/set-72157634328568268&quot;&gt;badge&lt;/a&gt; and swag bag containing an awesome shirt designed by &lt;a href=&quot;http://seanwes.com/&quot;&gt;Sean McCabe&lt;/a&gt;, some sponsor promos and deals, a notebook, and a whole bunch of awesome stickers. Then it was on to the &lt;a href=&quot;http://www.flickr.com/photos/charliebrwn1209/9138325708/in/set-72157634328568268&quot;&gt;awesome breakfast spread&lt;/a&gt;. Breakfast was a great time to start meeting new people or connecting with old friends depending on where you sat.&lt;/p&gt;
&lt;p&gt;After the delicious and fulfilling breakfast, everyone merged into the theatre to begin seating for the day&#39;s events. The &lt;a href=&quot;http://www.flickr.com/photos/charliebrwn1209/9136103085/in/set-72157634328568268/&quot;&gt;stage was set&lt;/a&gt; and, as the first speaker was preparing, the audience prepped their note-taking stations. There were the analog users, including myself, with Moleskins or other types of notebooks writing down their notes or taking sketch notes, and then there were various electronic options from iPad/tablet to full Macbook/laptop experiences. There were 7 speakers and a live podcast on set for the day, and I will just skim over some of my favorite takeaways overall.&lt;/p&gt;
&lt;p&gt;The conference started off strong with &lt;a href=&quot;http://www.flickr.com/photos/charliebrwn1209/9138327616/in/set-72157634328568268/&quot;&gt;Matt Graham&lt;/a&gt; from GitHub, which the Wall Street Journal humorously called a &amp;quot;Little-known social coding start-up&amp;quot;. He started a great discussion about the web community, especially around GitHub, and why we should all work together. I had played around on GitHub before but after this talk of being an industry craftsman and optimizing for happiness in order to create better tools for designers and developers worldwide, I was truly inspired to put more effort into my open source endeavors.&lt;/p&gt;
&lt;p&gt;The next big hit for me was &lt;a href=&quot;http://jennlukas.com/&quot;&gt;Jenn Lukas&lt;/a&gt;, the creator of &lt;a href=&quot;http://fuckyeahhovers.tumblr.com/&quot;&gt;Eff Yeah Hovers&lt;/a&gt;, who spoke about &lt;a href=&quot;http://www.flickr.com/photos/charliebrwn1209/9136113805/in/set-72157634328568268/&quot;&gt;Hot Links&lt;/a&gt; and how awesome hovers can be for user experience. They don&#39;t have to be cheesy or annoying; when done right, they can be delightful and appealing. The joy  exuded from Jenn speaking about these little additions to a site&#39;s design lit up the room, especially when the power went out and she continued on without much pause. The talk was finished off with a fun quiz created with CSS hovers, testing Chris Coyier, creator of &lt;a href=&quot;http://css-tricks.com/&quot;&gt;CSS-Tricks&lt;/a&gt; and &lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen&lt;/a&gt;, on his facial recognition of Tom Selleck &amp;amp; Burt Reynolds. I highly recommend visiting Eff Yeah Hovers and learn to love them again.&lt;/p&gt;
&lt;p&gt;After the amazing pizza lunch provided by &lt;a href=&quot;https://lessaccounting.com/&quot;&gt;Less Accounting&lt;/a&gt;, it was a real treat to witness a live recording of &lt;a href=&quot;http://shoptalkshow.com/&quot;&gt;The Shop Talk Show&lt;/a&gt;, hosted by &lt;a href=&quot;http://daverupert.com/&quot;&gt;Dave Rupert&lt;/a&gt; and Chris Coyier. They answered questions submitted during the week leading up to the event, including one submitted by me, and created some light-hearted discussion and hot drama in the theatre. It is definitely worth the listen if you never have before, and you may just become a fan of the duo (also you can find my question at 13:40 of the &lt;a href=&quot;http://shoptalkshow.com/episodes/live-from-front-end-conf/&quot;&gt;podcast&lt;/a&gt; ).&lt;/p&gt;
&lt;p&gt;The last great talk was done by &lt;a href=&quot;http://dougneiner.com/&quot;&gt;Doug Neiner&lt;/a&gt;, a real JQuery genius. He demonstrated various uses of JQuery for the average users and the professionals alike. I was having my own &lt;a href=&quot;http://hipsterbrown.com/anchor/anchor-cms-0-2.9/index.php/posts/pure-not-so-simple&quot;&gt;gripes&lt;/a&gt; with the overuse of the JavaScript library, but I kept an open mind about what Doug had to say. During the next 50 minutes, which included slides and an impressive live coding demo, I was throughly impressed with what he presented. Doug not only admitted to some of the useless aspects of the JS addition but showed off some of the more powerful properties of the language to make front-end development just a little more efficient. I can&#39;t say I&#39;ll be switching back to using it anytime soon, but when I do, I know I will appreciate it even more after that fantastic presentation.&lt;/p&gt;
&lt;p&gt;As the day finished out and everyone applauded the speakers one last time, it was time to prepare for the evening&#39;s activities,  starting with the Drinkup (meetup with drinks, get it?) sponsored by GitHub at the bar down the road. Every attendee received a pair of drink vouchers but the real friendly ones got some extras from the non-drinkers or DDs. This was one of my favorite parts of the day, not just for the free drinks, but it was a chance to chat with more of the attendees and connect with some of the awesome speakers I had heard earlier. I can say with confidence that people in the web industry are some of the most laid-back and easy-going folks I have ever encountered. It was not difficult or awkward to just walk about to a random group of strangers and join in on the conversation, or approach someone relatively famous within the web design world, like Chris Coyier, start a nice discussion. Conferences are not just about the food or the speakers but the designers, developers, and workers of the web, that have at least one similar interest as you, who will teach a little something and help you connect with other people in the industry as your career progresses. My only regret on day 1 was not being able to join in on the karaoke after the after party because of my long drive home.&lt;/p&gt;
&lt;h3&gt;Day 2&lt;/h3&gt;
&lt;p&gt;The final day of the conference was something truly unique to Front-End Conf and similar to a BarCamp event. Prior to the start of the conference, attendees can sign-up to give a 10  to 30 minutes talk on the subject of their choosing; this creates a ton of new and interesting discussions with much less effort on the host&#39;s part. The venue was different from day 1, but it was much more intimate with the audience and speaker on the same level; the variety of speaker backgrounds really stood out as well. The talks ranged from philosophical to technical to demonstrations of the latest technologies; here are a few of my favorites.&lt;/p&gt;
&lt;p&gt;JavaScript game development has peaked my interest lately, and &lt;a href=&quot;http://viget.com/about/team/dtello&quot;&gt;Dan Tello&lt;/a&gt; peaked it even further with his talk about the subject. Making a browser-based game with JS is one of the best ways to hone your skills in the language because you cannot use JQuery or CSS to control animation. After demonstrating the 8-bit style running game Dan and his team developed for a client, he showed off a bit of his open source game dev project called &lt;a href=&quot;https://github.com/greypants/blastEngine&quot;&gt;blastEngine&lt;/a&gt; on GitHub. In the project there is a demo space-shooter game to hack away on, as well as a basic framework to start a new game. I highly suggest giving it a fork, even just to play around with the demo.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://justinmezzell.com/&quot;&gt;Justin Mezzel&lt;/a&gt; is an extremely talented illustrator and all-around fun guy to talk to, and this event was the third time I have heard him speak to an audience. His usual talk being about designing with a narrative in mind, he strayed away from that subject and focused on designing as a community instead. It can be hard in such an independent industry, where most people just sit behind a computer screen all day, to reach out and depend on others to succeed. He spoke of his independent streak and, while he was fairly successful, it was not personally fulfilling. It wasn&#39;t until he would wake up in the morning with someone depending on him and vice versa, that he felt that he was actually contributing to the world and empowering the industry around him. Definitely very inspiring and along the theme of community I had heard throughout the weekend.&lt;/p&gt;
&lt;p&gt;At last, there was a great demonstration by a duo from a company called &lt;a href=&quot;http://mixture.io/&quot;&gt;Mixture&lt;/a&gt; based in the UK. They were showing off their very powerful tool by the same name as the company, which helps front-end developers prototype, create, and publish their various web products all within one app. It is currently a beta product but still powerful nonetheless. I had played around with it previously for a web app project but the demo showed how much more functionality there is in the current stages and the plans for the final product. The most impressive feature was the ability to connect multiple devices on the same wireless network and live edit on all the screens. It is a very neat tool to test out on a side project and then follow up into production work.&lt;/p&gt;
&lt;p&gt;My day was cut short due to the 4 hour drive to St. Augustine but it was still a full day of talks and connecting with more attendees, especially &lt;a href=&quot;http://www.flickr.com/photos/natecroft/9141419604/in/set-72157634330782555&quot;&gt;this guy&lt;/a&gt;, that left my satisfied with my purchase.&lt;/p&gt;
&lt;h2&gt;Recommendation&lt;/h2&gt;
&lt;p&gt;Overall, attending Front-End Conf 2013 was not only a fun and exciting experience but a productive trip as well. It was such a great first impression of web conferences that I cannot wait to find another of similar value and attend next year&#39;s event. I met designers &amp;amp; developers from Canada, the UK, and all over the United States that I can connect with on Twitter and make contact with later on in my career. It is not enough to just sit behind a computer and do good work anymore; being a people-person and making connections within the industry can lead to so much more opportunity later on.&lt;/p&gt;
&lt;p&gt;Finally, I would like to thank &lt;a href=&quot;http://dandenney.com/&quot;&gt;Dan Denney&lt;/a&gt;, his wife, &lt;a href=&quot;https://twitter.com/CherrieDenney&quot;&gt;Cherrie&lt;/a&gt;, and the whole team who helped organize such an amazing event. I cannot believe how lucky I was to stumble upon this opportunity and the great experience I had because of the people who run it. I hope to one day contribute something as awesome as Front-End Conf to the world of web design and development.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Pure &amp; Not-So-Simple</title>
    <link href="https://hipsterbrown.com/training-data/Pure-&amp;amp;-Not-So-Simple/"/>
    <updated>Mon, 17 Jun 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Pure-&amp;amp;-Not-So-Simple/</id>
    <category term="long-form"/>
    <content type="html">&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;I started taking a look at web design and development in January &#39;12; &lt;a href=&quot;http://www.codecademy.com/&quot;&gt;Codecademy&lt;/a&gt; had just launched and claimed a simpler way for the average person to learn JavaScript, so it seemed like the perfect beginning to my independent academic endeavor. However, I would soon find out that this method of learning JavaScript was just as insufficient as other similar services.&lt;/p&gt;
&lt;p&gt;Codecademy starts with the basics of syntax, as you would with learning any programming language, and advances with the typical functionality of JavaScript with loops, conditionals, and, obviously, functions. Along the way, you complete a few bits of practice code to prove a grasp of the content. The biggest takeaway from this type of learning is programming logic and problem solving, rather than how JavaScript works in the real world, and that is the biggest weakness as well. Just as the logic sections are finished, Codecademy immediately turns to JQuery for selector and event support, rather than giving a full background on JavaScript events and selectors, because it is a popular and simple library commonly placed hand-in-hand with JavaScript.&lt;/p&gt;
&lt;h2&gt;The Rant&lt;/h2&gt;
&lt;p&gt;That was a basic layout of what is wrong with trying to learn JavaScript in this day and age, at least on the web. JQuery is an awesome and powerful tool that many professional developers use to enhance experiences for all web users, but it is ultimately a tool to use with JavaScript, not in place of it. That line has been blurred, muddled, and obscured so much in the past few years that many job posts for developers/designers will include a preference for JavaScript/JQuery knowledge or even solely JQuery. So this was attitude I was surrounded by as I continued my quest into world of web development, ignorant to the fallacy I was basing my coding abilities upon.&lt;/p&gt;
&lt;p&gt;It wasn&#39;t until I wanted my web development to grow past simple menus and using plugins to solve my problems and try my hand at making actual web apps; instead, I realized how convoluted my self-driven education had been. Those EdTech services were not the only ones at fault, I had also gone into this learning experience the wrong way. I had no practical uses or intentions for learning web development, so all the lessons were just for show. It took time for me to realize just what web development and proper design was capable of. But getting out of the ditch I had dug myself was more difficult than could have been anticipated.&lt;/p&gt;
&lt;h2&gt;Loss of Words&lt;/h2&gt;
&lt;p&gt;It is not that the information is missing, it is more like it is far spread out. There are a sparse amount of real lessons on the subject; albeit a load of books, mostly from the O&#39;Reily franchise. But these books either cover a specific aspect of the language or additional tooling to use with it like Backbone.js or Node.js. It is rare to find a perfect resource that explains the exact practical use of JS in everyday websites and web apps without the inclusion of tools or libraries to spoil the fun. So the interested parties must search far and wide to fully grasp what native JavaScript is capable of nowadays, give it the old college try, and start getting creative, otherwise they can fall on the crutch of JQuery like so many others.&lt;/p&gt;
&lt;h2&gt;The Solution&lt;/h2&gt;
&lt;p&gt;Explore the far reaches and odd ends of the Internet to properly learn the basis of modern web development and understand what makes your favorite web tools tick. Fork a fancy project on GitHub and explore the source. Read as much as possible from articles, blog posts, and books by web professionals and experts alike. Come up with an idea for a simple web app and attempt to create it. After that is all said and done, then start exploring the use of additional tools and libraries to ease development while having a complete understanding of what it really takes to complete this task. JavaScript is an expansive, flexible, and powerful language for web development, especially in 2013, so it would be wise to take notice of how it operates now before you get left behind.&lt;/p&gt;
&lt;p&gt;P.S. My favorite resources so far are &lt;a href=&quot;http://javascriptissexy.com/&quot;&gt;JavaScript.is(Sexy)&lt;/a&gt;, &lt;a href=&quot;http://dailyjs.com/&quot;&gt;DailyJS&lt;/a&gt;, &lt;a href=&quot;http://www.amazon.com/gp/product/1118026691/ref=as_li_tf_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1118026691&amp;amp;linkCode=as2&amp;amp;tag=interhaptic-20&quot;&gt;Professional JavaScript for Web Developers&lt;/a&gt;, and &lt;a href=&quot;http://jsbooks.revolunet.com/&quot;&gt;JSbooks&lt;/a&gt;. Try using &lt;a href=&quot;http://codepen.io/&quot;&gt;CodePen&lt;/a&gt; to give your new code skills a shot.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Importance of Interaction</title>
    <link href="https://hipsterbrown.com/training-data/Importance-of-Interaction/"/>
    <updated>Wed, 29 May 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Importance-of-Interaction/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Since the fall of 2012, I have participated in the most important events in my career so far; my local design &amp;amp; tech meetups.&lt;/p&gt;
&lt;p&gt;My first meetup was for &lt;a href=&quot;http://www.meetup.com/Design-Orlando/&quot;&gt;Design Orlando&lt;/a&gt; at a local web/app consulting shop. Before this, I had had no face-to-face interaction to discuss various topics of the web industry and was seriously lacking in a connection to professionals in my area. How was I expected to jump into the real world in just a few years without any idea what it looked like to work in that space? It felt like a serious misstep in my education so thus far. So for this first meeting, I sat and listened to a short talk by a brilliant designer, &lt;a href=&quot;http://dribbble.com/JustinMezzell&quot;&gt;Justin Mezzel&lt;/a&gt;, who worked in the very shop we were all sitting in, and even commented a few times in the discussion afterwards. Anyone watching the event from afar might have thought it to be pretty mediocre, but in my mind, it was a spectacular display of the insight my area had to offer. It inspired me just as much the image of my first hand-coded website to keep pursuing a career in this industry and showed me that others are just as passionate about their jobs as I was.&lt;/p&gt;
&lt;p&gt;My participation in these events snowballed after the introductory experience. I am a consistent attendee of three monthly meetups, including Design Orlando, &lt;a href=&quot;http://www.meetup.com/Orlando-Tech/&quot;&gt;Orlando Tech Meetup&lt;/a&gt;, and &lt;a href=&quot;http://www.meetup.com/EdTech-Orlando/&quot;&gt;EdTech Orlando&lt;/a&gt;, and I continue to explore other opportunities as new groups pop up in the Orlando area. I have even participated in a &lt;a href=&quot;http://orlandoedu.startupweekend.org/&quot;&gt;Startup Weekend for EdTech startups&lt;/a&gt; and a mini-conference called &lt;a href=&quot;http://barcamporlando.org/&quot;&gt;BarCamp&lt;/a&gt;. As I continue to attend these events, I meet more people, offer more opinions, and become a familiar face for the local professionals. It has been and will continue one of the greatest advantages I have over my fellow college students. I could spend all my hours poring over a design or new web app idea, but when I graduate, I have links to my local industry and others around the country because I have those experiences that have  honed my people skills and helped me mature beyond the experience a classroom can provide.&lt;/p&gt;
&lt;p&gt;Most people go to the &amp;quot;rock-star&amp;quot; conferences to learn, connect, and have a good time with others in their field of work, but as a student, attending anything outside one&#39;s area means the extra cost of gas/flights and a hotel room on top of the triple-digit price tag for a ticket to these great events. Not many conferences will offer student discounts or scholarships, which means missing out on fantastic opportunities to learn and connect with others. Meetups are free and are attended by a lot of the same people you will see at the big conferences. Even if you can afford these mega meetups because you are an industry professional, lend a little of your time to attend a meetup in your area, or even start one, to create great discussions and contribute more informed society. Whether you are learning, teaching, or just hanging out and drinking, these meetups are a highly rewarding experience for anyone who attends because they are out of their homes, exploring their city, and interacting with new people that could change their lives for the better, without paying a dime. I suggest heading to &lt;a href=&quot;http://www.meetup.com/&quot;&gt;Meetup.com&lt;/a&gt; right now to search for something that dons your interest and give it a shot. Give me a shout @HipsterBrown if you find it works out in your favor.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Driving Towards Success</title>
    <link href="https://hipsterbrown.com/training-data/Driving-Towards-Success/"/>
    <updated>Fri, 24 May 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Driving-Towards-Success/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;When learning to drive and doing laps at the local driver&#39;s ed track, I was having trouble keeping in between the lines at the blistering speed of 15mph in the 2-ton death machine (also known as the family minivan) I had been given keys to. My father, who sat shotgun chuckling at my white-knuckled grip on the steering wheel, gave me a bit of advice: &amp;quot;If you just watch your side of the car and follow that line, the other side will be just fine.&amp;quot;* This little driving tip helped me in attaining the key the teenage freedom, my driver&#39;s license, and many years later, as I drive around a city 200+ miles from this event, I have realized it&#39;s application toward the industry I choose to work in.&lt;/p&gt;
&lt;p&gt;Startups are usually made up of a small team of thinks, doers, and creators that have passion and want to solve real world problems (or at least they should be). In many teams, there is the one person who leads them and drives them toward their future goals. This person might feel the need to make sure everything is running smoothly at all times and keeping the team in the lines on the way to success. Just like my first time behind the wheel, this can cause the driver to overcompensate on either side of lane, add frustration, and, if in real world situations, cause quite the collision. This is where the fatherly advice steps in, with a little tweaking on my part.&lt;/p&gt;
&lt;p&gt;While I tried out my dad&#39;s suggestion of &amp;quot;watching my line&amp;quot;, he would assure/caution me as I progressed. He would watch the line on his side and, when we finally got on the city roads, keep on eye on my blind spots as we traveled about. I would trust him to give me the correct information because I sure as hell couldn&#39;t see for myself and it was his life on the line too. For the driver of a startup, the people sitting in various passenger seats of this innovative new company is your team. They are there to assist you by watching your blind spots, maybe your weaker abilities of business deals they can manage or design needs that they can fulfill. Or at the occasional stoplight, things might get crazy with a &amp;quot;Chinese Fire Drill&amp;quot; of company roles and leave a new pilot behind the wheel but you keep going towards your destination. Your team, with trusted communication, will keep you balanced between those lines and on the road to success collision-free. So drive on, and make sure to blast some sweet tunes along the way.&lt;/p&gt;
&lt;p&gt;*I&#39;m don&#39;t recall if it actually rhymed but it sure sounds nice though.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Ignorance is Bliss</title>
    <link href="https://hipsterbrown.com/training-data/Ignorance-is-Bliss/"/>
    <updated>Tue, 21 May 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Ignorance-is-Bliss/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;The ignorance described in this piece is ignorance of walls, limitations, and naysayers. This ignorance supports continually learning for the sake of it and exploration into any field that fills your fancy. Ignorance is freedom.&lt;/p&gt;
&lt;p&gt;I started my exploration into web design and development in a Computer Fundamentals for Business class, which taught how to make a website for about 3 weeks of the course. This was the very basics, with HTML layouts built with tables and inline CSS; looking back, I realize how outdated and lazy it was for them to teach students this way. Nevertheless, I was hooked from the first time I saw my webpage completed in all its Web 1.0 glory. From then on, I began delving deeper into all that I could discover about building on the web.&lt;/p&gt;
&lt;p&gt;At no point in my journey did anyone tell me there was a difference between being a web designer and a web developer; in my ignorance, I had assumed that if you knew how to design a website in Photoshop then you would build it as well with HTML, CSS, and JavaScript. So I continued to learn the proper way to create web projects from mockups/wireframes to implementation with semantic code. I wasn’t working towards a job title, just trying whatever interested me.&lt;/p&gt;
&lt;p&gt;Looking through various design and development programs in higher education, or even at the job market in the industry, there are many constraints put on what you must know to earn that piece of paper or position. There is no standard definition on the requirements behind being a professional designer or supreme developer ninja (as so many of the uninformed like to all it); you must know Photoshop, even though you can perform the same tasks in other applications such as Sketch, or the school only teaches C and Java, when your dream job requires more modern languages like Ruby, Python, or PHP.&lt;/p&gt;
&lt;p&gt;Why do we confine ourselves into these barriers of knowledge instead of pushing past the limits of our job/degree requirements and keeping improving ourselves without someone saying it’s not necessary or just a waste of time? This may have just been a rant about personal journey into finding a job in the web world and the frustration at the various definition of what constitutes a web designer, front-end developer, or web developer, but the lesson I leave you with is to ignore these definititions and limits to explore what you want and enjoy what you do. Hopefully, this will leave you with bliss.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://hipsterbrown.com/training-data/Hello-World/"/>
    <updated>Mon, 13 May 2013 00:00:00 GMT</updated>
    <id>https://hipsterbrown.com/training-data/Hello-World/</id>
    <category term="long-form"/>
    <content type="html">&lt;p&gt;Well, here goes nothing. Hello World.&lt;/p&gt;
&lt;p&gt;About 2 months of planning, design, and development are temporarily finished as the v1.0-ish of HipsterBrown.com is live and ready to go. It has been almost a year since I created my first personal site, and since then I have learned a lot more in the ways of layouts, colors, icons, and front-end development in general. I hope to keep this design a bit longer than the last one but an avid web worker&#39;s site is never really done.&lt;/p&gt;
&lt;p&gt;Along with my Bio/Resume (which was the only content on the first site), I have included some of the design and web projects I have worked on over the past year, this awesome blog powered by &lt;a href=&quot;http://www.anchorcms.com/&quot; title=&quot;Anchor by Visual Idiot&quot;&gt;Anchor&lt;/a&gt;, and a simple contact form for getting in touch with me. Every page will continue to be improved upon and grow as more content is added.&lt;/p&gt;
&lt;p&gt;This was a great learning experience for me in project management, as I handled everything from start to finish, and showed me some of my weaker areas that I will pursue improvement upon. This blog will serve as a voice to share what I learn as I continue on my quest towards becoming a great front-end developer and designer. I have many other project ideas in the works, a few of which I hope to see into fruition during this summer, but I will always return to this site to make sure it is the best reflection of my work as possible.&lt;/p&gt;
&lt;p&gt;Keeping it classy,&lt;/p&gt;
&lt;p&gt;&lt;em&gt;HipsterBrown&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
</feed>
