<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
  <channel>
    <title>mjswensen.com</title>
    <link>https://mjswensen.com</link>
    <description>Matt Swensen, Front-end Software Engineer</description>
    <language>en-us</language>
    <pubDate>Mon, 15 Jun 2026 03:42:13 +0000</pubDate>
    <lastBuildDate>Mon, 15 Jun 2026 03:42:13 +0000</lastBuildDate>
    <ttl>60</ttl>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Jekyll</generator>
    <webMaster>swensen.matthew@gmail.com</webMaster>
    
    <item>
      <title>Rejoicing in His coming</title>
      <link>https://mjswensen.com/blog/rejoicing-in-his-coming/</link>
      <description>Christ did not come for humanity in the abstract. He came for people. For individuals with histories, contradictions, hopes, and fears. He came for you. Personally. With full awareness of who you are, with full awareness of what you carry, and full awareness of what you fear might disqualify you from His love. Because you matter.</description>
      <content:encoded>&lt;h1 id=&quot;intro&quot;&gt;Intro&lt;/h1&gt;

&lt;p&gt;Good morning!&lt;/p&gt;

&lt;p&gt;I could not for the life of me figure out how to start my talk. I rewrote this opening paragraph at least a few times and never could get anything that felt quite right. I kept trying to come up with a catchy hook because I wanted to do a good job and be impressive. Also, this message of God’s love that I want to share with you is so important.&lt;/p&gt;

&lt;p&gt;I couldn’t figure it out, so instead, I’m going to use someone else’s words to help me paint a picture of what it means to “rejoice.” The word “rejoice” is a word we hear often, especially at this time of year, but maybe don’t fully know what it means or looks like since we don’t use it in everyday speech. This is from a song called “My Everything” by Adam Young:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When my hope is lost
And my strength is gone
I run to you and you alone
When I can’t get up and I can’t go on
I run to you and you alone&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;‘Cause you’re my light in the dark
And I sing with all of my heart&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hallelujah
My almighty God divine
Hallelujah
I am yours and you are mine
This is all I know how to say
Hallelujah
Hallelujah
You’re my everything&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we don’t rejoice because life is easy, or because answers come quickly. We rejoice because Jesus Christ came, and we rejoice because He will come again.&lt;/p&gt;

&lt;h1 id=&quot;why-we-rejoice&quot;&gt;Why we rejoice&lt;/h1&gt;

&lt;p&gt;Jesus Christ did not come because humanity had proven itself worthy of rescue. He came because heaven does not abandon its children.&lt;/p&gt;

&lt;p&gt;This is where I want to slow down because I feel that it is critically important to understand.&lt;/p&gt;

&lt;p&gt;Christ did not come for humanity in the abstract. He came for people. For individuals with histories, contradictions, hopes, and fears. He came for you. Personally. With full awareness of who you are, with full awareness of what you carry, and full awareness of what you fear might disqualify you from His love. Because &lt;strong&gt;you matter.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-personal-experience&quot;&gt;A personal experience&lt;/h2&gt;

&lt;p&gt;I want to share something from my own life that I am still trying to figure out.&lt;/p&gt;

&lt;p&gt;There are days when I feel spiritually stretched thin. I’m trying to be present as a husband and father, faithful in my discipleship and ministering responsibilities, and yet I often feel as though I am carrying more than I am managing well. I find myself wondering if I am truly magnifying my calling. I make myself feel guilty for my minuscule ministering efforts.&lt;/p&gt;

&lt;p&gt;I believe in Christ. I trust His gospel. But sometimes I still wonder whether I am living it well. I suspect I’m not alone.&lt;/p&gt;

&lt;p&gt;And in moments like that, I personally don’t feel motivated by reminders that I should just try harder. What I need is the assurance that Christ did not come because I was sufficient. He came because I wasn’t.&lt;/p&gt;

&lt;p&gt;The angel’s declaration to the shepherds we remembered this week for Christmas was not conditional:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I bring you good tidings of great joy, which shall be to all people.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“All people” includes those who are striving but tired. Faithful but unsure. Devoted but, like me, still learning how to trust grace as much as effort.&lt;/p&gt;

&lt;p&gt;That is why we rejoice in His coming.&lt;/p&gt;

&lt;h1 id=&quot;how-we-rejoice&quot;&gt;How we rejoice&lt;/h1&gt;

&lt;p&gt;So then, how do we rejoice in His coming?&lt;/p&gt;

&lt;p&gt;The answer, mercifully, is simple.&lt;/p&gt;

&lt;p&gt;We rejoice by living close to Him.&lt;/p&gt;

&lt;p&gt;Not perfectly, or performatively, but intentionally.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We rejoice in His coming when we pray—not only because we are disciplined, but also because we are dependent.&lt;/li&gt;
  &lt;li&gt;We rejoice when we open the scriptures—to hear His voice and feel close to Him.&lt;/li&gt;
  &lt;li&gt;We rejoice when we partake of the sacrament—not only as a grounding regular routine, but as a warm, welcoming reunion with the Divine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-jewish-example&quot;&gt;A Jewish example&lt;/h2&gt;

&lt;p&gt;Recently, my sweetheart Christine has been teaching our family about different faith traditions, on what we have been calling our “holy envy tour.” Last week during Hanukkah we spent time learning about Judaism. Her insight that struck me most was the incredible depth of faith required by the Jewish people to continue waiting for the Messiah—century after century—without losing hope.&lt;/p&gt;

&lt;h2 id=&quot;a-latter-day-saint-perspective&quot;&gt;A Latter-day Saint perspective&lt;/h2&gt;

&lt;p&gt;As Latter-day Saints, we stand in a sacred place, where we no longer wait in uncertainty, because we know who the Messiah is. We know what He has done. We have the great blessing of scriptural evidence of His power, and of His mercy. And we we have some idea about what to look forward to when He comes again.&lt;/p&gt;

&lt;p&gt;My favorite part of all of the scriptures is when the people of the Book of Mormon rejoice in His visitation of the Americas. He asks them to come forward—one by one—to see Him and feel Him individually. This is from &lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/3-ne/11?lang=eng&amp;amp;id=p13-p17#p13&quot;&gt;3 Nephi chapter 11&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;13 And it came to pass that the Lord spake unto them saying:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;14 Arise and come forth unto me, that ye may thrust your hands into my side, and also that ye may feel the prints of the nails in my hands and in my feet, that ye may know that I am the God of Israel, and the God of the whole earth, and have been slain for the sins of the world.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;15 And it came to pass that the multitude went forth, and thrust their hands into his side, and did feel the prints of the nails in his hands and in his feet; and this they did do, going forth one by one until they had all gone forth, and did see with their eyes and did feel with their hands, and did know of a surety and did bear record, that it was he, of whom it was written by the prophets, that should come.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;16 And when they had all gone forth and had witnessed for themselves, they did cry out with one accord, saying:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;17 Hosanna! Blessed be the name of the Most High God! And they did fall down at the feet of Jesus, and did worship him.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For some, rejoicing might be outwardly loud and exuberant. After all, we believe that we cheered and jumped for joy at the presentation of the Plan of Salvation before coming to Earth. For me personally, it’s usually in the quiet moments that I feel like my heart is rejoicing the loudest.&lt;/p&gt;

&lt;h3 id=&quot;a-personal-experience-1&quot;&gt;A personal experience&lt;/h3&gt;

&lt;p&gt;Some months ago, on a particularly difficult day during a particularly difficult trial, I was driving in the car by myself. I remember praying to Heavenly Father, wondering when He was going to lift this trial from my family. I asked, “where &lt;em&gt;are&lt;/em&gt; You?” I think I even said it out loud. I had some music on, not spiritual music, just a regular song. Right then the song had this beautiful melody that captured my full attention. It felt like a bolt of lightning went through my heart when I felt the Spirit say, “I’m right here, in this melody. I’m in the trees and the air, I’m all around you. I’m here with you.” My heart rejoiced at the absolute miracle that a loving Creator of the Universe was aware of little me and took the time to speak to my heart.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Brothers and sisters, I testify that Jesus Christ came because the Father knows us each individually. I testify that His love is not theoretical and His grace is not distant. He came because you matter—and He continues to come now, quietly and faithfully into our hearts, until the day He comes again in person.&lt;/p&gt;

&lt;p&gt;Hallelujah.&lt;/p&gt;

&lt;p&gt;In the name of Jesus Christ, amen.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 28 Dec 2025 18:15:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/rejoicing-in-his-coming/</guid>
    </item>
    
    <item>
      <title>Clean + Format: a UX pattern for high-quality user input fields</title>
      <link>https://mjswensen.com/blog/clean-format-a-ux-pattern-for-high-quality-user-input-fields/</link>
      <description>Forms are at the core of how users get data into web applications. At Rivet, we've spent some time crafting what we believe is a nice balance between user-friendly, forgiving input fields and real-time validation and formatting.</description>
      <content:encoded>&lt;p&gt;Forms are at the core of how users get data into web applications. And there are so, so many ways to get the UX of text input fields wrong. At Rivet, we’ve spent some time crafting what we believe is a nice balance between user-friendly, forgiving input fields and real-time validation and formatting.&lt;/p&gt;

&lt;p&gt;The secret is simple on the surface but represents a novel insight: typically, the value of a text input is tied to a single piece of state, and all operations like formatting and validation are run against that state. Depending on the implementation, this can lead to user frustration as forms are validated prematurely or input formatted while the user is still trying to type. The answer to an improved experience lies in a two-step approach that separates the &lt;em&gt;cleaning&lt;/em&gt; and the &lt;em&gt;formatting&lt;/em&gt; of typed input, allowing users to type freely while ensuring data integrity &lt;em&gt;and&lt;/em&gt; (later) prettified display of the inputted data.&lt;/p&gt;

&lt;p&gt;This approach works for any type of field expecting a particular type of input data, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Currency input fields&lt;/li&gt;
  &lt;li&gt;Integer-only fields&lt;/li&gt;
  &lt;li&gt;Number fields with rounding to some maximum number of fractional digits&lt;/li&gt;
  &lt;li&gt;Fields with a maximum or minimum allowed value&lt;/li&gt;
  &lt;li&gt;Text fields where a particular case is enforced (uppercase-only, lowercase-only)&lt;/li&gt;
  &lt;li&gt;Phone number or email fields&lt;/li&gt;
  &lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive into how it works.&lt;/p&gt;

&lt;h2 id=&quot;secret-sauce&quot;&gt;Secret sauce&lt;/h2&gt;

&lt;p&gt;The core tenets of the approach are as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;In real time as the user types input, clean and convert the input into the desired computer-friendly value type on a best-effort basis. It’s important that this cleaning algorithm be deterministic and idempotent.
    &lt;ol&gt;
      &lt;li&gt;Emit this cleaned up value from the component immediately for use in the application.&lt;/li&gt;
      &lt;li&gt;In the case of two-way binding, this cleaned value might be immediately sent back into the component from the outer application—pass it through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; function again, and if the resulting value is the same as the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; on the user’s current raw input, do nothing (so as not to interrupt the user’s work in progress).&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Once the user leaves the field (on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blur&lt;/code&gt; event), pass the cleaned value to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt; function that produces a human-friendly stringified version of the cleaned value.
    &lt;ol&gt;
      &lt;li&gt;Overwrite the user’s raw input with this formatted value.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s a lot to grok—let’s break it down step by step as the user flows through a typical input experience.&lt;/p&gt;

&lt;h3 id=&quot;example-currency-field&quot;&gt;Example: currency field&lt;/h3&gt;

&lt;p&gt;Suppose we want to implement a user-friendly currency field for inputting USD values. Yes, a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input type=&quot;number&quot;&amp;gt;&lt;/code&gt; might suffice in some situations, but it leaves a lot to be desired in terms of user experience, particularly with the visual formatting of user input. So we use an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input type=&quot;text&quot;&amp;gt;&lt;/code&gt; as the core of our custom component and plug in our approach as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The user types in their currency value, but makes an error or two on the way, typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;12,34.567a&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;We don’t get in the user’s way and try to format or validate their input. Instead, we make a best guess effort at what they were intending. This is our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; function:
    &lt;ol&gt;
      &lt;li&gt;We’ll use an algorithm that filters out non-digit characters except &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt;, leaving us with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1234.567&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;We’ll parse this new string as a number and use a rounding function to coerce our number into a maximum of two fractional digits, leaving us with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1234.57&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;This is our best guess at the user’s intention. We’ll emit this cleaned numeric value to the application.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;The application may send this value back to us, or it may send us a different value. This is how we’ll react to values coming in programmatically:
    &lt;ol&gt;
      &lt;li&gt;We’ll run the value coming in through our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; function, and we’ll also run the user’s current raw input through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; function. If the two results are an exact match, we’ll assume the application is just sending us back the value we just emitted, and do nothing. This is because if we format it and update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, the user’s work will be interrupted and their cursor position will change. If it’s different, however, we can assume that the application state has changed in some way—perhaps we loaded some data from the server and need to update the state of this field—so we’ll go ahead and run this value through our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt; function (more on this below) and overwrite the user’s text in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Once the user leaves the input field (on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blur&lt;/code&gt; event), we’ll take our most recent result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; and pass it through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt;, and update the value of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. This is what the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt; function will do:
    &lt;ol&gt;
      &lt;li&gt;Our cleaned, computer-friendly value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1234.57&lt;/code&gt;. We want to turn this into something more human-friendly with some typical currency formatting. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Intl&lt;/code&gt; API or any number formatting library to stringify this value into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$1,234.57&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;We write the formatted value to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt; for display.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s what it looks like in action. Note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt;ed value echoed back to the UI under the field for demonstration purposes.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/currency-field.gif&quot; alt=&quot;Currency field demonstration&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;precise-semantics-of-clean--format&quot;&gt;Precise semantics of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;So how do you determine how to implement your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format()&lt;/code&gt; functions, and which bits of processing should belong in each one?&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
    &lt;table&gt;
        &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;Function&lt;/th&gt;
                &lt;th&gt;&lt;code&gt;clean()&lt;/code&gt;&lt;/th&gt;
                &lt;th&gt;&lt;code&gt;format()&lt;/code&gt;&lt;/th&gt;
            &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
            &lt;tr&gt;
                &lt;td&gt;Purpose&lt;/td&gt;
                &lt;td&gt;Takes raw user input and converts it into a computer-friendly value for use by the application.&lt;/td&gt;
                &lt;td&gt;Takes the cleaned value from `clean()` and formats it into a human-friendly string.&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Implementation responsibility&lt;/td&gt;
                &lt;td&gt;Likely to be bespoke but could be delegated to a library depending on the needs of the input.&lt;/td&gt;
                &lt;td&gt;Unless very specific formatting is needed, likely to be delegated to a formatting library or language/platform API.&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Idempotent?&lt;/td&gt;
                &lt;td&gt;Yes&lt;/td&gt;
                &lt;td&gt;No&lt;/td&gt;
            &lt;/tr&gt;
            &lt;tr&gt;
                &lt;td&gt;Deterministic?&lt;/td&gt;
                &lt;td&gt;Yes&lt;/td&gt;
                &lt;td&gt;Yes&lt;/td&gt;
            &lt;/tr&gt;
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Creating user-friendly input fields is a delicate balancing act. By separating the cleaned computer-friendly value from the user’s raw input state, we ensure that they can type freely without interruptions, while also maintaining data integrity and sending the correct data type to the outer application for immediate use. After the user is done working we can present neatly formatted results. This approach can be applied to various types of input fields; wherever some additional structure is needed on top of the raw input text.&lt;/p&gt;
</content:encoded>
      <pubDate>Wed, 07 Aug 2024 03:42:50 +0000</pubDate>
      <guid>https://mjswensen.com/blog/clean-format-a-ux-pattern-for-high-quality-user-input-fields/</guid>
    </item>
    
    <item>
      <title>The single most important factor that differentiates front-end frameworks</title>
      <link>https://mjswensen.com/blog/the-single-most-important-factor-that-differentiates-front-end-frameworks/</link>
      <description>There are tons of blog posts on the internet about how frameworks differ and which one to pick for your next web project. Usually they cover a few aspects of the framework like syntax, development setup, and community size. This isn't one of those posts.</description>
      <content:encoded>&lt;p&gt;There are tons of blog posts on the internet about how frameworks differ and which one to pick for your next web project. Usually they cover a few aspects of the framework like syntax, development setup, and community size.&lt;/p&gt;

&lt;p&gt;This isn’t one of those posts.&lt;/p&gt;

&lt;p&gt;Instead, we’ll go directly to the crux of the main problem front-end frameworks set out to solve: &lt;em&gt;change detection&lt;/em&gt;, meaning &lt;em&gt;detecting changes to application state so that the UI can be updated accordingly.&lt;/em&gt; Change detection is the fundamental feature of front-end frameworks, and the framework authors’ solution to this one problem determines everything else about it: developer experience, user experience, API surface area, community satisfaction and involvement, etc., etc.&lt;/p&gt;

&lt;p&gt;And it turns out that examining various frameworks from this perspective will give you all of the information you need to determine the best choice for you and for your users. So let’s dive deep into how each framework tackles change detection.&lt;/p&gt;

&lt;h2 id=&quot;major-frameworks-compared&quot;&gt;Major frameworks compared&lt;/h2&gt;

&lt;p&gt;We’ll look at each of the major players and how they have tackled change detection, but the same critical eye can apply to any front-end JavaScript framework you may come across.&lt;/p&gt;

&lt;h3 id=&quot;react&quot;&gt;React&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I’ll manage state so that I know when it changes.” —React&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;True to its de-facto tagline, change detection in React is “just JavaScript.” Developers simply update state by calling directly into the React runtime through its API; since React is notified to make the state change, it also knows that it needs to re-render the component.&lt;/p&gt;

&lt;p&gt;Over the years, the default style for writing components has changed (from &lt;a href=&quot;https://react.dev/reference/react/Component#defining-a-class-component&quot;&gt;class components&lt;/a&gt; and &lt;a href=&quot;https://react.dev/reference/react/PureComponent#purecomponent&quot;&gt;pure components&lt;/a&gt; to &lt;a href=&quot;https://react.dev/reference/react/Component#migrating-a-simple-component-from-a-class-to-a-function&quot;&gt;function components&lt;/a&gt; to &lt;a href=&quot;https://react.dev/reference/react#state-hooks&quot;&gt;hooks&lt;/a&gt;) but the core principle has remained the same. Here’s an example component that implements a button counter, written in the hooks style:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;decrement&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;increment&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;increment later&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The key piece here is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setCount&lt;/code&gt; function returned to us by React’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useState&lt;/code&gt; hook. When this function is called, React can use its internal virtual DOM diffing algorithm to determine which pieces of the page to re-render. Note that this means the React runtime has to be included in the application bundle downloaded by the user.&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Conclusion&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;React&#39;s change detection paradigm is straightforward: the application state is maintained inside the framework (with APIs exposed to the developer for updating it) so that React knows when to re-render.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;angular&quot;&gt;Angular&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I’ll make the developer do all the work.” —Angular&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you scaffold a new Angular application, it appears that change detection happens automagically:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;div&amp;gt;
      &amp;lt;button (click)=&quot;count = count - 1&quot;&amp;gt;decrement&amp;lt;/button&amp;gt;
      &amp;lt;span&amp;gt;{{ count }}&amp;lt;/span&amp;gt;
      &amp;lt;button (click)=&quot;count = count + 1&quot;&amp;gt;increment&amp;lt;/button&amp;gt;
      &amp;lt;button (click)=&quot;incrementLater()&quot;&amp;gt;increment later&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;incrementLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;What’s really happening, is that Angular uses &lt;a href=&quot;https://angular.io/guide/zone&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NgZone&lt;/code&gt;&lt;/a&gt; to observe user actions, and is checking &lt;em&gt;your entire component tree on every event.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For applications of any reasonable size, this causes performance issues, since checking the entire tree quickly becomes too costly. So Angular provides an escape hatch from this behavior by allowing the developer to choose a different change detection strategy: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnPush&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnPush&lt;/code&gt; means that the onus is on the developer to inform Angular when state changes so that Angular can re-render the component. Aside from the default naive strategy, &lt;a href=&quot;https://angular.io/api/core/ChangeDetectionStrategy&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnPush&lt;/code&gt; is the only other change detection strategy Angular offers&lt;/a&gt;. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OnPush&lt;/code&gt; enabled, we must manually tell Angular’s change detector to check the new state if it ever gets updated asynchronously:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;div&amp;gt;
      &amp;lt;button (click)=&quot;count = count - 1&quot;&amp;gt;decrement&amp;lt;/button&amp;gt;
      &amp;lt;span&amp;gt;{{ count }}&amp;lt;/span&amp;gt;
      &amp;lt;button (click)=&quot;count = count + 1&quot;&amp;gt;increment&amp;lt;/button&amp;gt;
      &amp;lt;button (click)=&quot;incrementLater()&quot;&amp;gt;increment later&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;changeDetection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OnPush&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ChangeDetectorRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;incrementLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cdr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;markForCheck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For applications of any reasonable complexity, this approach quickly becomes untenable.&lt;/p&gt;

&lt;p&gt;Alternative solutions are introduced to wrangle this problem. The primary one that the Angular docs suggest is to use RxJS observables in conjunction with the &lt;a href=&quot;https://angular.io/api/common/AsyncPipe&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncPipe&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;DECREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;INCREMENT_LATER&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;div&amp;gt;
      &amp;lt;button (click)=&quot;update.next(Action.DECREMENT)&quot;&amp;gt;decrement&amp;lt;/button&amp;gt;
      &amp;lt;span&amp;gt;{{ count | async }}&amp;lt;/span&amp;gt;
      &amp;lt;button (click)=&quot;update.next(Action.INCREMENT)&quot;&amp;gt;increment&amp;lt;/button&amp;gt;
      &amp;lt;button (click)=&quot;update.next(Action.INCREMENT_LATER)&quot;&amp;gt;increment later&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;changeDetection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ChangeDetectionStrategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OnPush&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Counter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Subject&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;switchScan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;switch &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;DECREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;INCREMENT_LATER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prev&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;startWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Under the hood, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncPipe&lt;/code&gt; takes care of subscribing to the observable, informing the change detector when the observable emits a new value, and unsubscribing when the component is destroyed. Observables are a powerful way to model state changes over time, but they come with some serious drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;They are difficult to debug.&lt;/li&gt;
  &lt;li&gt;They have a very steep learning curve.&lt;/li&gt;
  &lt;li&gt;They are great for modeling streams of values (think: mouse movements), but they are overkill for the more common use cases (simple state changes like the on/off state of a checkbox).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To overcome the shortcomings of the default change detection paradigm, the Angular team is working on a new approach called &lt;a href=&quot;https://angular.io/guide/signals&quot;&gt;Signals&lt;/a&gt;. Conceptually, signals are similar to Svelte stores (which we’ll get to later), and fundamentally, they solve the change detection problem the same way as React; the framework is taking control over the application’s state so that changes can be easily monitored and re-renders can be as efficient as possible.&lt;/p&gt;

&lt;p&gt;From the Angular docs:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Angular Signals is a system that granularly tracks how and where your state is used throughout an application, allowing the framework to optimize rendering updates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a large paradigm shift, making Angular applications more similar to the other frameworks.&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Conclusion&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Angular&#39;s change detection is a disaster. The developer gets two suboptimal choices: (1) the slow and naive default implementation, or the complexity of managing change detection manually. Signals will make it much better, though nearly a decade too late.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;vue&quot;&gt;Vue&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I’ll track changes to state and react accordingly.” —Vue&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vue’s approach to change detection is subtly different than both React and Angular. Rather than calling a framework function to change state (React) or changing state and then informing the framework that it has been changed (Angular), you work with state objects that have been specially instrumented by the framework to intercept and detect changes.&lt;/p&gt;

&lt;p&gt;Confusingly, Vue has two different APIs that wrap the same underlying change detection engine differently. Under the “Options API,” you define an object that contains your state, and Vue assigns a proxied version of that object as a member of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt; for use in the component’s functions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;decrement&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ count }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;increment&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;incrementLater&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment later&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;incrementLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alternatively, the “Composition API” is somewhat similar to React’s hooks: a framework function is called to retrieve a state object that Vue can monitor for changes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;vue&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;incrementLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;decrement&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ count }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;increment&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;incrementLater&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment later&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;https://vuejs.org/guide/essentials/reactivity-fundamentals.html#why-refs&quot;&gt;Conceptually, the object returned from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref()&lt;/code&gt; has a getter and a setter for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt;&lt;/a&gt;, which allows Vue to track changes to it.&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Conclusion&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Vue utilizes JavaScript language features to allow developers to work with stateful variables without thinking about change detection.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h3 id=&quot;svelte&quot;&gt;Svelte&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I’ll figure it out for you at compile time.” —Svelte&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the surface, Svelte’s version of our counter component looks pretty similar to the other frameworks:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;incrementLater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;on:click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{decrement}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;decrement&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{count}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;on:click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{increment}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;on:click=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{incrementLater}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;increment later&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But Svelte’s approach to change detection is completely novel in comparison. At compile time, Svelte analyzes an AST (Abstract Syntax Tree) of the component’s code and &lt;em&gt;injects some code into the compiled output&lt;/em&gt; that surgically updates the DOM when necessary. For example, here is what the compiled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decrement()&lt;/code&gt; function looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;$invalidate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$$invalidate&lt;/code&gt; is a call to Svelte’s internals to instruct the compiled component to update the DOM.&lt;/p&gt;

&lt;p&gt;This compile-time approach means that Svelte applications don’t need to bundle a large runtime along with the application itself.&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Conclusion&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Svelte strikes a rare win-win balance: developers don&#39;t have to think about change detection at all, and can interact with stateful variables intuitively; yet the end user&#39;s experience is improved through better performance because a bare-minimum application (with change detection baked in) is shipped to the browser.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;so-what&quot;&gt;So, what?&lt;/h2&gt;

&lt;p&gt;The nuances of how various frameworks choose to tame this beast is not limited to how things work at the component level; it ripples out to &lt;em&gt;everything else&lt;/em&gt; about the framework. To name just a few examples: the concepts used to &lt;a href=&quot;https://react.dev/learn/reusing-logic-with-custom-hooks&quot;&gt;create custom React hooks&lt;/a&gt; composed of the basic hooks provided by React out of the box are not relevant to generalizing component behavior in Vue; the challenge of working with observables for state management in Angular has led folks to &lt;a href=&quot;https://github.com/futhark/ngx-observable-input&quot;&gt;try and find ways to convert component input props to observables&lt;/a&gt;; the framework’s API, dictated by its change detection management paradigm, affects how well it integrates with productivity and quality tools like typechecking, testing, and linting. And so on, and so forth.&lt;/p&gt;

&lt;p&gt;And those are just examples from the developer’s point of view. Each approach has implications on the performance of the application for the end user. React, Vue, and Angular each ship a runtime to the user’s browser that needs to be parsed and executed. Svelte’s choice to be a compile-time framework obviates this need in most cases, so the user gets a faster loading experience. Each framework has subtleties that make it more susceptible to particular classes of bugs (often around state management or change detection) that the end user will experience.&lt;/p&gt;

&lt;p&gt;Find a change detection paradigm that fits the needs of your application, and everything else will fall into place. Pick one that doesn’t work, and you’ll be fighting against it for the life of the project.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 11 Jun 2023 05:14:49 +0000</pubDate>
      <guid>https://mjswensen.com/blog/the-single-most-important-factor-that-differentiates-front-end-frameworks/</guid>
    </item>
    
    <item>
      <title>How to set up a WireGuard VPN for personal use</title>
      <link>https://mjswensen.com/blog/how-to-set-up-a-wireguard-vpn-for-personal-use/</link>
      <description>VPNs are a great way to level up your privacy online. Here's how to set one up from scratch.</description>
      <content:encoded>&lt;p&gt;VPNs are a great way to level up your privacy online. Here’s how to set one up from scratch.&lt;/p&gt;

&lt;h2 id=&quot;step-1-spin-up-and-connect-to-your-cloud-server&quot;&gt;Step 1: spin up and connect to your cloud server&lt;/h2&gt;

&lt;p&gt;Using your cloud provider of choice, provision a new server instance. Since the VPN will be for personal use only, inexpensive instances will work fine. Select any operating system that WireGuard supports; we’ll assume Ubuntu for this tutorial.&lt;/p&gt;

&lt;p&gt;Connect to your server via SSH as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; user.&lt;/p&gt;

&lt;div class=&quot;cards tip&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Root user&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Heads up, all commands outlined in this tutorial assume the root user, so you won&#39;t see any commands prefixed with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;step-2-install-wireguard&quot;&gt;Step 2: install WireGuard&lt;/h2&gt;

&lt;p&gt;If you picked Ubuntu for your server’s OS, you can install WireGuard with one command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;wireguard&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;cards tip&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Working directory&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;For the remaining steps, change your working directory to &lt;code&gt;/etc/wireguard&lt;/code&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;step-3-generate-a-key-pair-for-your-vpn-server&quot;&gt;Step 3: generate a key pair for your VPN server&lt;/h2&gt;

&lt;p&gt;First, generate a private key:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;wg genkey &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; private.key&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then, use the private key to derive a public key:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;wg pubkey &amp;lt; private.key &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; public.key&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;These are the keys your VPN server will use when encrypting traffic with your client VPN peers.&lt;/p&gt;

&lt;h2 id=&quot;step-4-create-your-server-configuration-file&quot;&gt;Step 4: create your server configuration file&lt;/h2&gt;

&lt;p&gt;With WireGuard installed and keys generated, we’re ready to create our VPN configuration.&lt;/p&gt;

&lt;p&gt;First, find the name of the public network interface:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ip route list default&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The output might look something like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default via ... dev eth0 proto static&lt;/code&gt;—the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; part is what we’re after.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg0.conf&lt;/code&gt; with the following contents, being sure to include the private key where required, and replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; with the name of the network interface:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;[&lt;span class=&quot;n&quot;&gt;Interface&lt;/span&gt;]
&lt;span class=&quot;n&quot;&gt;SaveConfig&lt;/span&gt; = &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PostUp&lt;/span&gt; = &lt;span class=&quot;n&quot;&gt;ufw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;route&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wg0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PostUp&lt;/span&gt; = &lt;span class=&quot;n&quot;&gt;iptables&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nat&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;I&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;POSTROUTING&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MASQUERADE&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PreDown&lt;/span&gt; = &lt;span class=&quot;n&quot;&gt;ufw&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;route&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delete&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wg0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PreDown&lt;/span&gt; = &lt;span class=&quot;n&quot;&gt;iptables&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nat&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;D&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;POSTROUTING&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt; -&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MASQUERADE&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ListenPort&lt;/span&gt; = &lt;span class=&quot;m&quot;&gt;51820&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contents&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;private&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-5-configure-ip-forwarding&quot;&gt;Step 5: configure IP forwarding&lt;/h2&gt;

&lt;p&gt;Add the following line at the bottom of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;&lt;span class=&quot;n&quot;&gt;net&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;ipv4&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;ip_forward&lt;/span&gt;=&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sysctl -p&lt;/code&gt; to load the new configuration.&lt;/p&gt;

&lt;h2 id=&quot;step-6-allow-udp-traffic&quot;&gt;Step 6: allow UDP traffic&lt;/h2&gt;

&lt;p&gt;Update the operating system firewall to allow traffic on the port specified in the WireGuard configuration:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ufw allow 51820/udp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Restart UFW to pick up the new configuration:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;ufw disable
ufw &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-7-configure-the-wireguard-server-to-start-on-boot&quot;&gt;Step 7: configure the WireGuard server to start on boot&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;systemctl &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;wg-quick@wg0.service&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-8-generate-keys-for-clients&quot;&gt;Step 8: generate keys for clients&lt;/h2&gt;

&lt;p&gt;Now we’ll generate keys for the devices that will be connecting to the server. Follow this process for as many clients as you need:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Output the private key with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg genkey&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Output the corresponding public key with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo -n &#39;&amp;lt;the private key&amp;gt;&#39; | wg pubkey&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Mark the public key as allowed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg set wg0 peer &amp;lt;the public key&amp;gt; allowed-ips 10.8.0.2&lt;/code&gt;; for each client increment the last digit of the IP address&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verify that the public keys have been added as peers by running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg&lt;/code&gt; command with no arguments. If you’ve configured three clients, the output should look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot; data-lang=&quot;plaintext&quot;&gt;interface: wg0
  public key: &amp;lt;the server&#39;s public key&amp;gt;
  private key: (hidden)
  listening port: 51820

peer: &amp;lt;first client&#39;s public key&amp;gt;
  allowed ips: 10.8.0.2/32

peer: &amp;lt;second client&#39;s public key&amp;gt;
  allowed ips: 10.8.0.3/32

peer: &amp;lt;third client&#39;s public key&amp;gt;
  allowed ips: 10.8.0.4/32&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-9-configure-clients&quot;&gt;Step 9: configure clients&lt;/h2&gt;

&lt;p&gt;Finally, configure your client devices to connect to your WireGuard VPN server. This process will be a little different depending on whether you are using the iOS app, Android app, or another Linux installation of WireGuard. Generally, though, your client configuration should look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;[&lt;span class=&quot;n&quot;&gt;Interface&lt;/span&gt;]
&lt;span class=&quot;n&quot;&gt;PrivateKey&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&amp;gt;
&lt;span class=&quot;n&quot;&gt;Address&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&amp;gt;/&lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DNS&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DNS&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolvers&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;you&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wish&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;use&lt;/span&gt; (&lt;span class=&quot;n&quot;&gt;optional&lt;/span&gt;)&amp;gt;

[&lt;span class=&quot;n&quot;&gt;Peer&lt;/span&gt;]
&lt;span class=&quot;n&quot;&gt;PublicKey&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&amp;gt;
&lt;span class=&quot;n&quot;&gt;Endpoint&lt;/span&gt; = &amp;lt;&lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&amp;gt;:&lt;span class=&quot;m&quot;&gt;51820&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AllowedIPs&lt;/span&gt; = &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;/&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0/0&lt;/code&gt; configuration value indicates that all internet traffic should be tunnelled through the VPN connection.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Happy tunneling!&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 19 Mar 2023 00:04:28 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-to-set-up-a-wireguard-vpn-for-personal-use/</guid>
    </item>
    
    <item>
      <title>Cascade layers, <code>subgrid</code>, and container queries: What's new in CSS</title>
      <link>https://mjswensen.com/blog/cascade-layers-subgrid-and-container-queries-whats-new-in-css/</link>
      <description>As CSS inches closer to its 30th birthday, it continues to evolve and improve more rapidly than ever.</description>
      <content:encoded>&lt;p&gt;As CSS inches closer to its 30th birthday, it continues to evolve and improve more rapidly than ever. Let’s take a cursory look at some of its newest features being shipped in the near future: cascade layers, subgrids, and container queries.&lt;/p&gt;

&lt;h2 id=&quot;cascade-layers-in-css&quot;&gt;Cascade layers in CSS&lt;/h2&gt;

&lt;p&gt;First, here’s a quick background on what exactly we mean by “cascade.”&lt;/p&gt;

&lt;p&gt;Styles can come from a few different sources, such as the browser’s defaults or the stylesheets we author as web developers. Some of these sources take precedence over others. Traditionally, we think of the cascade as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;User-agent stylesheet (browser defaults)&lt;/li&gt;
  &lt;li&gt;User settings&lt;/li&gt;
  &lt;li&gt;Author stylesheets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Styles in the website author’s stylesheets take precedence over the browser defaults. (This is not to be confused with &lt;a href=&quot;https://css-tricks.com/specifics-on-css-specificity/&quot;&gt;selector specificity&lt;/a&gt;, which is resolved within each layer of the cascade.)&lt;/p&gt;

&lt;p&gt;What the new cascade layers feature allows us to do as CSS authors is add and control additional layers of the cascade. So, rather than all of a website’s styles being lumped into a single cascade layer, authors can now create multiple cascade layers and control their order of precedence:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;User-agent stylesheet (browser defaults)&lt;/li&gt;
  &lt;li&gt;User settings&lt;/li&gt;
  &lt;li&gt;Author layer “C”&lt;/li&gt;
  &lt;li&gt;Author layer “B”&lt;/li&gt;
  &lt;li&gt;Author layer “A”&lt;/li&gt;
  &lt;li&gt;Etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before we dive into the syntax of defining cascade layers, let’s look at some use cases and problems you might solve with additional control over the cascade.&lt;/p&gt;

&lt;h3 id=&quot;why-use-cascade-layers-in-css&quot;&gt;Why use cascade layers in CSS?&lt;/h3&gt;

&lt;p&gt;Cascade layers allow us to circumvent some of the thorniest parts of working in CSS code. In some cases, we will be able to avoid the use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; because basic, overridable styles can simply be placed in a lower-precedence cascade layer.&lt;/p&gt;

&lt;p&gt;For example, you might consider placing typical “reset” styles in their own cascade layer, and styles from a third-party CSS library in another. You could also put utility classes — such as those used by “atomic CSS” approaches like Tailwind CSS — into their own cascade layer to take precedence over common styles without the need for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another use case could be dividing up an existing design system into different layers to allow for easy theming and customization. It will be interesting to see how modern JavaScript frameworks, which typically ship with workarounds like CSS modules or style encapsulation via emulated shadow DOM, adopt cascade layers to improve their developer and user experiences — although these features might be affected more directly by the &lt;a href=&quot;https://drafts.csswg.org/css-scoping/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@scope&lt;/code&gt; proposal&lt;/a&gt;, which is different but related.&lt;/p&gt;

&lt;h3 id=&quot;syntax&quot;&gt;Syntax&lt;/h3&gt;

&lt;p&gt;We define cascade layers and the styles that belong in them with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@layer&lt;/code&gt; statement. Although not required, you can declare multiple layers upfront (without any styles) to determine their order of precedence, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will create three layers called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reset&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utilities&lt;/code&gt;. Styles defined in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;utilities&lt;/code&gt; will take precedence over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt; styles, which will take precedence over styles in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reset&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To add styles to a layer, we define them in a block, like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;utilities&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that because we declared the layers previously and also defined their order (the initial line: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@layer reset, base, utilities;&lt;/code&gt;), any paragraphs on our page will be blue, even though the styles for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt; layer are defined later. Had we omitted that line, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base&lt;/code&gt; layer would take precedence because it does appear later in our stylesheet, and the paragraphs would therefore be red.&lt;/p&gt;

&lt;p&gt;It’s also possible to create anonymous layers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Or load external stylesheets into a particular layer:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;k&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;utilities.css&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;utilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can also nest layers within other layers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;innerOne&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;/* ... */&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;/* Dot syntax works, too. */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@layer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;innerTwo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* ... */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are several options and plenty of flexibility!&lt;/p&gt;

&lt;h3 id=&quot;when-can-i-use-cascade-layers-in-css&quot;&gt;When can I use cascade layers in CSS?&lt;/h3&gt;

&lt;p&gt;Pretty soon! At the time of writing, cascade layers have already shipped to most evergreen browsers, with broader support on the way. See resources like &lt;a href=&quot;https://caniuse.com/css-cascade-layers&quot;&gt;caniuse.com&lt;/a&gt; for updated browser support information.&lt;/p&gt;

&lt;h3 id=&quot;one-last-important-note&quot;&gt;One last &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; note&lt;/h3&gt;

&lt;p&gt;With the addition of &lt;a href=&quot;https://blog.logrocket.com/how-css-works-understanding-the-cascade-d181cd89a4d8/&quot;&gt;multiple cascade layers&lt;/a&gt;, a deeper understanding of how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; works is much more, well, important. For a clear and well-written explanation (including visuals) on the nuances of how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; fits into the cascade and its effect on cascade layers, be sure to check out &lt;a href=&quot;https://css-tricks.com/css-cascade-layers/&quot;&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;introducing-csss-subgrid&quot;&gt;Introducing CSS’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subgrid&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;This new feature is much simpler — at least conceptually — than cascade layers, but is certainly still exciting and much anticipated by CSS authors. Currently, grid row and column configurations only extend to the immediate children of a grid container.&lt;/p&gt;

&lt;p&gt;Those children can also be grid containers themselves, but to achieve a consistent alignment with the parent grid, they need to carefully configure their columns or rows to match precisely those of the parent. Sometimes that’s difficult or impossible, depending on the grid settings.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/nested-grid.png&quot; alt=&quot;It&#39;s sometimes difficult to sync nested grids.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With the addition of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subgrid&lt;/code&gt; value for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grid-template-rows&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grid-template-columns&lt;/code&gt;, grid children can now inherit the grid row and column settings from the parent and properly position their children seamlessly and effortlessly, without the need to define or keep in sync with their own row and column settings. Nice!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/subgrid.png&quot; alt=&quot;Subgrids are perfect for when nested grids need to stay in sync.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For a more detailed explanation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subgrid&lt;/code&gt;, see &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid&quot;&gt;“Subgrid” on the MDN docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;when-can-i-use-subgrid&quot;&gt;When can I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subgrid&lt;/code&gt;?&lt;/h3&gt;

&lt;p&gt;At the time of writing, only Firefox implements the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subgrid&lt;/code&gt; value, but the other evergreen browsers won’t be far behind.&lt;/p&gt;

&lt;h2 id=&quot;mastering-container-queries-in-css&quot;&gt;Mastering container queries in CSS&lt;/h2&gt;

&lt;p&gt;When browsers shipped support for media queries, the web took a giant step forward in the realm of responsive design. Media queries solved a myriad of problems, but they also exposed a new problem with complicated responsive layouts: context-aware responsiveness.&lt;/p&gt;

&lt;p&gt;For example, you might have a component that exists within the main part of your web page and needs to change its layout based on the width of that main section. However, the width of the main section is itself dependent on the width of the browser viewport and is controlled by its own responsive layout settings.&lt;/p&gt;

&lt;p&gt;With today’s media queries, the only way to change the layout of your inner component is to do so based on changes to the browser viewport’s width, which means you need to keep its media queries carefully in sync with those of the main section so that your component always looks correct at every viewport size. And, if you ever change your main layout, you’ll need to update your component, too.&lt;/p&gt;

&lt;p&gt;Container queries solve this problem by allowing CSS authors to respond to the dimensions of arbitrary elements, not solely those of the viewport. This will decouple components styles from their parent containers, making responsive components vastly easier to maintain — and reduce visual layout bugs!&lt;/p&gt;

&lt;h3 id=&quot;when-can-i-use-container-queries&quot;&gt;When can I use container queries?&lt;/h3&gt;

&lt;p&gt;Unfortunately, the specification is still under active development, and no browsers have yet implemented container queries, as the final syntax is still being refined. However, it’s one of the most widely requested features, so we should see regular progress and big updates on container queries in the near future.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;CSS is rapidly evolving to provide better primitives for us web developers and our users. Keep an eye on these top features for your next project!&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 07 May 2022 01:25:47 +0000</pubDate>
      <guid>https://mjswensen.com/blog/cascade-layers-subgrid-and-container-queries-whats-new-in-css/</guid>
    </item>
    
    <item>
      <title>Extending object-like types with interfaces in TypeScript</title>
      <link>https://mjswensen.com/blog/extending-object-like-types-with-interfaces-in-typescript/</link>
      <description>Interfaces are one of TypeScript's core features, allowing developers to flexibly and expressively enforce constraints on their code to reduce bugs and improve code readability.</description>
      <content:encoded>&lt;p&gt;Interfaces are one of TypeScript’s core features, allowing developers to flexibly and expressively enforce constraints on their code to reduce bugs and improve code readability. Let’s dive into exploring interfaces’ characteristics and how we might better leverage them in our programs.&lt;/p&gt;

&lt;h2 id=&quot;what-are-interfaces&quot;&gt;What are interfaces?&lt;/h2&gt;

&lt;p&gt;First, a little background. Interfaces are a way for developers to name a type for later reference in their programs. For example, a public library’s management software might have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Book&lt;/code&gt; interface for data representing books in the library’s collection:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;isbn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With it, we can ensure that book data in the program contains the essential information of title, author, and ISBN. If it doesn’t, the TypeScript compiler will throw an error:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;A Tale of Two Cities&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Charles Dickens&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Error: Property &#39;isbn&#39; is missing in type &#39;{ title: string; author: string; }&#39; but required in type &#39;Book&#39;.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;interfaces-vs-types&quot;&gt;Interfaces vs. types&lt;/h3&gt;

&lt;p&gt;If you’ve written TypeScript code before, you might be familiar with type aliases, another common way to name a type, and you might be asking, “Why use interfaces over types or vice versa?”&lt;/p&gt;

&lt;p&gt;The primary difference is that interfaces can be reopened for adding additional properties (via &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/declaration-merging.html&quot;&gt;declaration merging&lt;/a&gt;) in different parts of your program, while type aliases cannot. Let’s look at how to take advantage of declaration merging, as well as some cases of when you might want to.&lt;/p&gt;

&lt;h2 id=&quot;expanding-interfaces-in-typescript&quot;&gt;Expanding interfaces in TypeScript&lt;/h2&gt;

&lt;h3 id=&quot;option-1-declaration-merging&quot;&gt;Option 1: Declaration merging&lt;/h3&gt;

&lt;p&gt;As noted above, interfaces can be reopened to add new properties and expand the definition of the type. Here is a nonsensical example to illustrate this capability:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Of course, it’s not likely that the same interface would be reopened nearby like this. It would be clearer to simply define it in a single statement:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stock&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;tickerSymbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, when would you want to expand an interface in different parts of your program? Let’s take a look at a real-world use case.&lt;/p&gt;

&lt;h4 id=&quot;declaration-merging-to-wrangle-disparate-user-preferences&quot;&gt;Declaration merging to wrangle disparate user preferences&lt;/h4&gt;

&lt;p&gt;Suppose you are writing a React application, and you need some pages that will allow the user to configure information such as their profile, notification preferences, and accessibility settings.&lt;/p&gt;

&lt;p&gt;For clarity and user experience, you’ve split up these three concerns into three separate pages where the source code will be in three files: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Profile.tsx&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notifications.tsx&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accessibility.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From an application architecture perspective, it would be nice if all of the user’s preferences were contained in a single object that adheres to an interface we’ll call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt;. This way, you can easily load and save the preferences object with your backend API with just one or two endpoints rather than several.&lt;/p&gt;

&lt;p&gt;The next question is: “Where should the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt; interface be defined?” You could put the interface in its own file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferences.ts&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt; it into the three pages — or, you could take advantage of declaration merging and have each page define only the properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt; that it cares about, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Profile.tsx&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Preferences&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;avatarUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Profile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... UI for managing the user&#39;s profile ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Notifications.tsx&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Preferences&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;smsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;emailEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Notification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... UI for managing the user&#39;s notification preferences ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Accessibility.tsx&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Preferences&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;highContrastMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Accessibility&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... UI for managing the user&#39;s accessibility settings ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In the end, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt; interface will resolve to fully contain all the properties, as desired:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Preferences&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;avatarUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;smsEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;emailEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;highContrastMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the UI code is now co-located with only the properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt; it is responsible for managing, making the program easier to understand and maintain. Nice!&lt;/p&gt;

&lt;h3 id=&quot;option-2-extending-interfaces-in-typescript&quot;&gt;Option 2: Extending interfaces in TypeScript&lt;/h3&gt;

&lt;p&gt;Another way to expand interfaces in TypeScript is to mix one or more of them into a new interface.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Dog&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;breed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Fish&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Pet&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;finColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;betta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Fish&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Sophie&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;finColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This probably looks familiar to object-oriented programmers. However, interfaces offer a key feature that is not typically found in traditional object-oriented programming: multiple inheritance.&lt;/p&gt;

&lt;p&gt;Multiple inheritance allows us to combine behaviors and properties of multiple interfaces into a single interface. Let’s look at a use case for when you might want to do this.&lt;/p&gt;

&lt;h4 id=&quot;extending-interfaces-to-form-a-type-safe-global-state-store&quot;&gt;Extending interfaces to form a type-safe global state store&lt;/h4&gt;

&lt;p&gt;Suppose that you’re building an application that enables users to keep track of their to-do lists and their daily schedules in one place. You’ll have some different UI components for tracking each of those tasks:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// todo-list.ts&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoListItem&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoListItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ... application code for managing to-do lists ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// calendar.ts&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CalendarEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CalendarEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ... application code for managing the calendar ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that you’ve created the basic interfaces for keeping track of your two pieces of state, you would like a single interface that represents the state of the entire application. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extends&lt;/code&gt; keyword to create such an interface. We’ll also add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modified&lt;/code&gt; field so that we know when our state was last updated:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppState&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;modified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now you can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppState&lt;/code&gt; interface to ensure that the application is properly handling the state.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... save the state to a storage layer ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Text Marcy&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-05&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Buy groceries&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Study&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-11 08:00:00&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-11 10:00:00&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;modified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-06&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;extending-types&quot;&gt;Extending types&lt;/h4&gt;

&lt;p&gt;While re-opening interfaces is not possible with type aliases, this approach of extending types is, but with some subtle differences in syntax. Here’s the equivalent example adapted to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interface&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoListItem&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoListItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CalendarEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CalendarEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ToDoList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;modified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... save the state to a storage layer ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;todos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Text Marcy&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-05&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Buy groceries&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;completedDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Study&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-11 08:00:00&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-11 10:00:00&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;modified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2022-02-06&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;There are a few different ways to extend object-like types with interfaces in TypeScript, and, in some cases, you may be able to use type aliases. In those cases, even the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces&quot;&gt;official docs say they are mostly interchangeable&lt;/a&gt;, at which point it’s down to style, preference, organization, habit, etc. But if you’d like to declare different properties on the same type in different parts of your program, use interfaces.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 03 Apr 2022 00:09:22 +0000</pubDate>
      <guid>https://mjswensen.com/blog/extending-object-like-types-with-interfaces-in-typescript/</guid>
    </item>
    
    <item>
      <title>How to develop over HTTPS on localhost</title>
      <link>https://mjswensen.com/blog/how-to-develop-over-https-on-localhost/</link>
      <description>There are a number of reasons you may wish to use HTTPS as part of your web application development workflow. Find out how to set up SSL locally.</description>
      <content:encoded>&lt;p&gt;There are a number of reasons you may wish to use HTTPS as part of your web application development workflow:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Your app will likely be deployed to users via HTTPS; the closer your development environment is to production, the less likely you are to run into bugs.&lt;/li&gt;
  &lt;li&gt;Browsers behave differently in secure contexts, and you want to avoid production-only surprises.&lt;/li&gt;
  &lt;li&gt;You might be integrating with third-party services or APIs that require the use of HTTPS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep reading to find out how to set up SSL for local development.&lt;/p&gt;

&lt;h2 id=&quot;step-1-generate-a-self-signed-ssl-certificate&quot;&gt;Step 1: Generate a self-signed SSL certificate&lt;/h2&gt;

&lt;p&gt;Unless you are developing on a &lt;a href=&quot;https://youtu.be/zivPD-LL57k&quot;&gt;remote development environment&lt;/a&gt; with a static IP address, you won’t have a unique domain name that points to your development environment (and even if you are, you may not want or need one). Without a unique domain name, our only option for and SSL certificate is a self-signed one for local use only. Use this command to generate one:&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;tl;dr&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Use the command below to generate your self-signed certificate and private key.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;openssl req &lt;span class=&quot;nt&quot;&gt;-x509&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-out&lt;/span&gt; localhost.crt &lt;span class=&quot;nt&quot;&gt;-keyout&lt;/span&gt; localhost.key &lt;span class=&quot;nt&quot;&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class=&quot;nt&quot;&gt;-nodes&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-subj&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/CN=localhost&#39;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-extensions&lt;/span&gt; EXT &lt;span class=&quot;nt&quot;&gt;-config&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[req]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;distinguished_name = dn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[dn]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN=localhost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[EXT]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;subjectAltName=DNS:localhost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;keyUsage=digitalSignature&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;extendedKeyUsage=serverAuth&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s pick that command apart. Feel free to &lt;a href=&quot;#step-2-install-and-trust-the-certificate&quot;&gt;skip to step 2&lt;/a&gt; if you don’t care about the details.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl req&lt;/code&gt; - We’re using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; program to generate our certificate. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl&lt;/code&gt; has many sub-commands; the one we are using is &lt;a href=&quot;https://www.openssl.org/docs/man1.1.1/man1/openssl-req.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;req&lt;/code&gt;, which is for generating certificates and certificate requests&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-x509&lt;/code&gt; - Output a self-signed certificate (rather than a certificate signing request that you might send off to a certificate authority—often called a “CA” for short—to be verified and signed for production use).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-out localhost.crt&lt;/code&gt; - Write the certificate to a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost.crt&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-keyout localhost.key&lt;/code&gt; - Write the private key to a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost.key&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-newkey rsa:2048&lt;/code&gt; - Generate and use a new private key (as opposed to an existing key, which you’d specify with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-key&lt;/code&gt; option) of 2048 bits using the RSA algorithm.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-nodes&lt;/code&gt; - Do not encrypt the private key.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-subj &#39;/CN=localhost&#39;&lt;/code&gt; - Sets the subject of the certificate request. For production certs, this would include information about the company or business seeking the certificate (such as location, department, email address, etc.), but since this is a development-only certificate, the only necessary information is the “common name” (“CN” for short), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-extensions EXT&lt;/code&gt; - Specifies that the extensions section of our config is delineated with a header titled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXT&lt;/code&gt;. That config is defined in the next part of the command.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-config &amp;lt;(printf &quot;...&quot;)&lt;/code&gt; - The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-config&lt;/code&gt; option takes a file name containing configuration options, but rather than use a separate file we use input redirection (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; character) and a subshell (the command embedded between parentheses, which returns the string config) to pass the config contents directly, for convenience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contents of the configuration are shown more readably below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;[req]
distinguished_name = dn
[dn]
CN=localhost
[EXT]
subjectAltName=DNS:localhost
keyUsage=digitalSignature
extendedKeyUsage=serverAuth&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[req]&lt;/code&gt; - Options for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;req&lt;/code&gt; subcommand are below this heading.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distinguished_name = dn&lt;/code&gt; - Indicates that the distinguished name fields will be found in a section of the config titled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dn&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[dn]&lt;/code&gt; - The heading for the fields as indicated above.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CN=localhost&lt;/code&gt; - The common name (“CN”) for the certificate; in this case: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[EXT]&lt;/code&gt; - The heading for the extensions, as indicated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-extensions&lt;/code&gt; parameter above.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subjectAltName=DNS:localhost&lt;/code&gt; - Indicates that the certificate should match the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; domain name.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyUsage=digitalSignature&lt;/code&gt; - Indicates that the key may be used for signing the certificate.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extendedKeyUsage=serverAuth&lt;/code&gt; - Indicate that in addition to signing the certificate, the private key will also be used to identify the server for SSL/TLS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-2-install-and-trust-the-certificate&quot;&gt;Step 2: Install and trust the certificate&lt;/h2&gt;

&lt;p&gt;Now your local operating system needs to know that the certificate is trustworthy. (Otherwise you’ll see a warning when you access &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://localhost&lt;/code&gt; that the certificate is not trusted. This warning can be bypassed but trusting the certificate at the OS level gives a smoother development experience.)&lt;/p&gt;

&lt;p&gt;For macOS, this can be achieved by following these steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the Keychain Access app.&lt;/li&gt;
  &lt;li&gt;Drag and drop your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost.crt&lt;/code&gt; file onto the app.&lt;/li&gt;
  &lt;li&gt;Right click on the new certificate entry and choose “Get Info”.&lt;/li&gt;
  &lt;li&gt;Expand the “Trust” section of the Get Info dialog.&lt;/li&gt;
  &lt;li&gt;For “Secure Sockets Layer (SSL)”, choose “Always Trust”.&lt;/li&gt;
  &lt;li&gt;Close the dialog and input your password (or TouchID) to save the changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There should be a similar process for trusting the certificate in Windows and Linux.&lt;/p&gt;

&lt;p&gt;If you’re working on a team, write some documentation for your teammates on how to do this (or point them to this article) so that new engineers can get up and running quickly.&lt;/p&gt;

&lt;h2 id=&quot;step-3-configure-your-development-server-to-use-the-certificate-and-private-key&quot;&gt;Step 3: Configure your development server to use the certificate and private key&lt;/h2&gt;

&lt;p&gt;Lastly, your development server applications need to be updated to use the certificate and private key. This will be different for each language and framework. Here are some examples:&lt;/p&gt;

&lt;p&gt;For Netlify, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify dev&lt;/code&gt; server can be configured to use your SSL certificate in development by adding the following to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netlify.toml&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;k&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;certFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;localhost.crt&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;keyFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;localhost.key&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For Node.js servers, &lt;a href=&quot;https://nodejs.org/dist/latest-v16.x/docs/api/https.html#httpscreateserveroptions-requestlistener&quot;&gt;pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cert&lt;/code&gt; options to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https.createServer&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once configured, you should be able to access your development server via HTTPS on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;!&lt;/p&gt;

&lt;h2 id=&quot;additional-reading&quot;&gt;Additional reading&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://letsencrypt.org/docs/certificates-for-localhost/&quot;&gt;Certificates for localhost article by Let’s Encrypt&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.openssl.org/docs/man1.1.1/man1/openssl-req.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openssl req&lt;/code&gt; manual page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
      <pubDate>Fri, 11 Mar 2022 15:16:41 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-to-develop-over-https-on-localhost/</guid>
    </item>
    
    <item>
      <title>Mastering mapped types in TypeScript</title>
      <link>https://mjswensen.com/blog/mastering-mapped-types-in-typescript/</link>
      <description>Mapped types are a handy TypeScript feature that allow for types to be automatically derived from other types.</description>
      <content:encoded>&lt;p&gt;Mapped types are a handy TypeScript feature that allow authors to keep their types &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot;&gt;DRY (“Don’t Repeat Yourself”)&lt;/a&gt;. However, because they tow the line between programming and &lt;a href=&quot;https://en.wikipedia.org/wiki/Metaprogramming&quot;&gt;metaprogramming&lt;/a&gt;, they can be difficult to understand at first.&lt;/p&gt;

&lt;p&gt;In this post, we’ll cover some foundational concepts that enable mapped types, then walk through an advanced, real-world example.&lt;/p&gt;

&lt;h2 id=&quot;why-use-mapped-types-in-typescript&quot;&gt;Why use mapped types in TypeScript?&lt;/h2&gt;

&lt;p&gt;Using mapped types in a program is especially useful when there is a need for a type to be derived from (and remain in sync with) another type.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Configuration values for the current user&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Whether or not the user has permission to change configuration values&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppPermissions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;changeUsername&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;changeLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This example is problematic because there is an implicit relationship between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppConfig&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppPermissions&lt;/code&gt;. Whenever a new configuration value is added to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppConfig&lt;/code&gt;, there must also be a corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boolean&lt;/code&gt; value in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppPermissions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is better to have the type system manage this relationship than to rely on the discipline of future program editors to make the appropriate updates to both types simultaneously.&lt;/p&gt;

&lt;p&gt;We’ll delve into the specifics of the mapped types syntax later on, but here is a preview of the same example using mapped types instead of explicit types:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Configuration values for the current user&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Whether or not the user has permission to change configuration values&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppPermissions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Property&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`change&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Capitalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Property&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;foundational-concepts-of-mapped-types&quot;&gt;Foundational concepts of mapped types&lt;/h2&gt;

&lt;p&gt;Mapped types build upon each of these concepts and TypeScript features.&lt;/p&gt;

&lt;h3 id=&quot;what-is-a-mapped-type&quot;&gt;What is a mapped type?&lt;/h3&gt;

&lt;p&gt;In a computer science context, the term “map” means to transform one thing into another, or, more commonly, refers to turning similar items into a different list of transformed items. Likely the most familiar application of this idea is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array.prototype.map()&lt;/code&gt;, which is used in everyday TypeScript and JavaScript programming:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Yields [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here we’ve mapped each number in the array to its string representation. So a mapped type in TypeScript means we’re taking one type and transforming it into another type by applying a transformation to each of its properties.&lt;/p&gt;

&lt;h3 id=&quot;indexed-access-types-in-typescript&quot;&gt;Indexed access types in TypeScript&lt;/h3&gt;

&lt;p&gt;TypeScript authors can access the type of a property by looking it up by name:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case, the resolved type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Username&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt;. For more information on indexed access types, see the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html&quot;&gt;official docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;index-signatures&quot;&gt;Index signatures&lt;/h3&gt;

&lt;p&gt;Index signatures are handy for cases when the actual names of the type’s properties are not known, but the type of data they will reference is known.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Foo Bar&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentLang&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this example, the TypeScript compiler reports that the type of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentLang&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;any&lt;/code&gt;. This functionality, in conjunction with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyof&lt;/code&gt; operator detailed below, is one of the core components that make mapped types possible. For more information on index signatures, see the official documentation on &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures&quot;&gt;object types&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;using-union-types-in-typescript&quot;&gt;Using union types in TypeScript&lt;/h3&gt;

&lt;p&gt;A union type is a combination of two or more types. It signals to the TypeScript compiler that the type of the underlying value could be any one of the types included in the union. This is a valid TypeScript program:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StringOrNumberUnion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StringOrNumberUnion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hello, world!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here is a more complicated example that shows some of the advanced protection the compiler can offer with union types:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;species&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AnimalOrPerson&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Animal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AnimalOrPerson&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;loadFromSomewhereElse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// No problem, both Animal and Person have the name property.&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Compilation error; value might not have the age property if it is an Animal.&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// No problem, TS knows that value has the age property, and therefore it must be a Person if we&#39;re inside this if block.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;See the docs on &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types&quot;&gt;everyday types&lt;/a&gt; for more information on union types.&lt;/p&gt;

&lt;h3 id=&quot;using-the-keyof-type-operator&quot;&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyof&lt;/code&gt; type operator&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyof&lt;/code&gt; type operator returns a union of the keys of the type passed to it. For example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfigKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AppConfigKey&lt;/code&gt; type resolves to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;username&quot; | &quot;layout&quot;&lt;/code&gt;. Note that this also works in tandem with index signatures:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserPreferenceKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;preferences&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserPreferenceKey&lt;/code&gt; type resolves to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string | number&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt; because accessing JavaScript object properties by number is valid syntax). Read about the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/keyof-types.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyof&lt;/code&gt; type operator&lt;/a&gt; here.&lt;/p&gt;

&lt;h2 id=&quot;mapped-types-a-real-world-example&quot;&gt;Mapped types: a real-world example&lt;/h2&gt;

&lt;p&gt;Now that we’ve covered the foundations upon which TypeScript’s mapped types feature is built, let’s walk through a detailed real-world example. Suppose our program keeps track of electronic devices and their manufacturers and prices. We might have a type like this to represent each device:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, we’d like to ensure that we have a way to display those devices to the user in a human-readable format, so we’ll add a new type for an object that can format each property of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt; with an appropriate formatting function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Capitalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s pull this code block apart, piece by piece.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Key in keyof Device&lt;/code&gt; uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keyof&lt;/code&gt; type operator to generate a union of all keys in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt;. Putting it inside of an index signature essentially iterates through all properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt; and maps them to properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format${Capitalize&amp;lt;Key&amp;gt;}&lt;/code&gt; is the transformation part of the mapping and uses &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/mapped-types.html#key-remapping-via-as&quot;&gt;key remapping&lt;/a&gt; and &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html&quot;&gt;template literal types&lt;/a&gt; to change the property name from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formatX&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(value: Device[Key]) =&amp;gt; string;&lt;/code&gt; is where we utilize the indexed access type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device[Key]&lt;/code&gt; to indicate that the format function’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; parameter is of the type of the property we are formatting. So, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formatManufacturer&lt;/code&gt; takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; (the manufacturer) while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formatPrice&lt;/code&gt; takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt; (the price).&lt;/p&gt;

&lt;p&gt;Here’s what the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt; type looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatManufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;formatPrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, let’s suppose we add a third property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;releaseYear&lt;/code&gt;, to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt; type:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Thanks to the power of mapped types, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt; type is automatically expanded to look like this without any additional work on our part:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatManufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;formatPrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;formatReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Any implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt; must add the new function or compilation will fail. Voilà!&lt;/p&gt;

&lt;h2 id=&quot;bonus-a-reusable-formatter-type-with-generics&quot;&gt;Bonus: a reusable formatter type with generics&lt;/h2&gt;

&lt;p&gt;Suppose now that our program not only needs to keep track of electronic devices but also accessories for those devices:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Again, we want a type for an object that can provide string formatting functions for all the properties of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accessory&lt;/code&gt;. We could implement an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccessoryFormatter&lt;/code&gt; type, similar to how we implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt;, but we end up with mostly duplicate code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AccessoryFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Capitalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The only difference is that we’ve replaced references to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt; type with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accessory&lt;/code&gt;. Instead, we can create a generic type that takes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Device&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Accessory&lt;/code&gt; as a type argument and produces the desired mapped type. Traditionally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; is used to represent the type argument.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Capitalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that we have to make one slight change to our property name transformation. Because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; could be any type, we don’t know for sure that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Key&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; (for example, arrays have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt; properties), so we take the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types&quot;&gt;intersection&lt;/a&gt; of the property name and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt; to satisfy the constraint imposed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Capitalize&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/generics.html&quot;&gt;TypeScript documentation on generics&lt;/a&gt; for more detail on how they work. Now we can replace our bespoke implementations of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DeviceFormatter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AccessoryFormatter&lt;/code&gt; to use the generic type instead:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AccessoryFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here is the full final code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;releaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;as &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Capitalize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Device&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AccessoryFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Formatter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Accessory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;deviceFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DeviceFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatManufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;manufacturer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatPrice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatReleaseYear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;accessoryFormatter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AccessoryFormatter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;formatSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; inches`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/play?ssl=29&amp;amp;ssc=1&amp;amp;pln=1&amp;amp;pc=1#code/C4TwDgpgBAIhBuBLAxtAvFA3gKClAtgIYB2ArgGaHLAQBOdAXFAM7C2LEDmA3LlGO1RMy+AEZ1eeegBsIhZhACac2sNJiJ2AL69soSFACCyVM2YB7WiCgYceZOemWmrdl0ktEALwhqNtXh1sPXBoADFLImAaWgAeABUAPhssPgBtAGkIaw4oAGts83IoeKh5KAADckjCYAASTABhQjBEYEJpbwhYrOsAMhY2Dk5ErQqAXSYACngO0l8SzOzxgEobZNdhwN19aDgkVAjaKJiUo5O6WP2UCETeXaMTCDNLEHPa04x36MvjUwsrHdgg5iKwoAATBA3b4xJjXQ41H60FJ2KDVY61ACyJAoVBijCgUyIZEo1FI9FoazQyWJuLJFIANHx0VEAAqCBZTAQ3KnJCp1Brc1AAOmA5jCiAAHhBwVMAEwrMZMvAs2oAJQgsnkShU0xAKl5UH1hFoovMAGUhlwpismUFsCCwVR-q8YQS-s8AW9EZ9UirEY1HM5CQ4nJT1lBQ5ZlWjEeautNmF1DfzMEmfFooBxkAALZ4VO28IA&quot;&gt;Try this code in the TypeScript playground on typescriptlang.org.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Mapped types provide a powerful way to keep related types in sync automatically. They can also help prevent bugs by keeping types DRY and obviating the need to repetitively type (or copy and paste) similar property names.&lt;/p&gt;

&lt;p&gt;Happy typing!&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 18 Feb 2022 20:17:28 +0000</pubDate>
      <guid>https://mjswensen.com/blog/mastering-mapped-types-in-typescript/</guid>
    </item>
    
    <item>
      <title>How to set up a simple web server development container for VS Code</title>
      <link>https://mjswensen.com/blog/simple-web-server-development-container-vs-code/</link>
      <description>Sometimes it's nice to have a lightweight development container that automatically serves the contents of a directory, for rapid prototyping or building static web pages—without having to setup a tool chain on your local environment or install web server software on your local machine.</description>
      <content:encoded>&lt;p&gt;Sometimes it’s nice to have a lightweight development container that automatically serves the contents of a directory, for rapid prototyping or building static web pages—without having to setup a tool chain on your local environment or install web server software (and its dependencies) on your local machine.&lt;/p&gt;

&lt;p&gt;If you’re using VS Code’s extension for developing inside containers, below is a recipe for a comfortable development setup containing these three main attributes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Lightweight; small image footprint and minimal container boot up time&lt;/li&gt;
  &lt;li&gt;Automatic; once the container starts, the files are being served on a local port without the need to run another command&lt;/li&gt;
  &lt;li&gt;Complete; has all the dependencies required by VS Code for the full development lifecycle&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve this, we’ll use the &lt;a href=&quot;https://alpinelinux.org/&quot;&gt;Alpine Linux&lt;/a&gt; operating system with &lt;a href=&quot;https://nginx.org/en/&quot;&gt;Nginx&lt;/a&gt; as our development web server.&lt;/p&gt;

&lt;h2 id=&quot;step-1-define-development-container-in-a-dockerfile&quot;&gt;Step 1: define development container in a Dockerfile&lt;/h2&gt;

&lt;p&gt;We’ll include the &lt;a href=&quot;https://code.visualstudio.com/docs/remote/linux#_remote-host-container-wsl-linux-prerequisites&quot;&gt;dependencies required by VS Code’s remote containers extension&lt;/a&gt; as well as the specific tools we need for development.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer/Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span class=&quot;c&quot;&gt;# We&#39;ll base our dev container image on Alpine Linux.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; alpine:3.15&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Then we&#39;ll add the dependencies we need:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - musl, libgcc, and libstdc++ are required by VS Code&#39;s server&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - git and gnupg are useful for remote development so that we&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#   can commit and sign commits from within the container&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - and of course nginx itself&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apk add &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  musl &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  libgcc &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  libstdc++ &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  git &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  gnupg &lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  nginx

&lt;span class=&quot;c&quot;&gt;# Finally, we&#39;ll remove nginx&#39;s default content and replace it&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# with a symlink to our source code.&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /usr/share/nginx/html
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; /workspaces/my-app /usr/share/nginx/html&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;cards tip&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;div class=&quot;card-title&quot;&gt;Heads up&lt;/div&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;You&#39;ll want to replace &lt;code&gt;/workspaces/my-app&lt;/code&gt; with whatever directory VS Code mounts your source code to inside the container.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;step-2-configure-vs-code-to-utilize-our-image&quot;&gt;Step 2: configure VS Code to utilize our image&lt;/h2&gt;

&lt;p&gt;With our image definition done, we’ll add some configuration to instruct VS Code to run it.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.devcontainer/devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;my-app&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Tell&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;VS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;our&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;our&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Dockerfile.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;./Dockerfile&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Automatically&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;expose&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;(mapped&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;container&#39;s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;nginx&#39;s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;default)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;appPort&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;8080:80&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Make&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;sure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;our&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;nginx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;container&#39;s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;runs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;which&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;starts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;up&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;background.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;overrideCommand&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-3-profit&quot;&gt;Step 3: profit&lt;/h2&gt;

&lt;p&gt;With those files in place, VS Code has everything it needs to spin up the development container with the source code mounted inside. Once it’s running, you can load up your project’s files in the browser (or even try VS Code’s built-in development browser) on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8080&lt;/code&gt; for easy side-by-side development:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/simple-web-server-development-container-vs-code.png&quot; alt=&quot;screenshot of code and browser side-by-side&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Happy prototyping!&lt;/p&gt;
</content:encoded>
      <pubDate>Wed, 26 Jan 2022 16:18:46 +0000</pubDate>
      <guid>https://mjswensen.com/blog/simple-web-server-development-container-vs-code/</guid>
    </item>
    
    <item>
      <title>Using tsm as a shebang interpreter in TypeScript</title>
      <link>https://mjswensen.com/blog/using-tsm-as-a-shebang-interpreter-in-typescript/</link>
      <description>Executing TypeScript code typically involves transpiling it down to vanilla JavaScript for use in common environments like Node.js or the browser. tsm is a TypeScript module loader for Node.js, providing native TypeScript support without the need to transpile it to JavaScript first.</description>
      <content:encoded>&lt;h2 id=&quot;using-tsm-as-a-shebang-interpreter-in-typescript&quot;&gt;Using tsm as a shebang interpreter in TypeScript&lt;/h2&gt;

&lt;p&gt;TypeScript has taken the web development world by storm, becoming increasingly popular for enterprise projects and solo developers alike.&lt;/p&gt;

&lt;p&gt;Executing TypeScript code typically involves transpiling it down to vanilla JavaScript for use in common environments like Node.js or the browser. This approach still represents the vast majority of how TypeScript programs are run, but a few projects have emerged that allow developers to run their TypeScript code directly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/lukeed/tsm/blob/master/docs/usage.md&quot;&gt;tsm&lt;/a&gt; is a brand new player in this category and represents a step forward in the broader TypeScript/JavaScript ecosystem.&lt;/p&gt;

&lt;h3 id=&quot;what-is-tsm&quot;&gt;What is tsm?&lt;/h3&gt;

&lt;p&gt;tsm is a TypeScript module loader for Node.js, created by &lt;a href=&quot;https://twitter.com/lukeed05&quot;&gt;Luke Edwards&lt;/a&gt;. It expands Node.js to provide native TypeScript support without the need to transpile it to JavaScript first.&lt;/p&gt;

&lt;p&gt;Rather, tsm uses &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt; under the hood to quickly transpile TypeScript sources into JavaScript on the fly before passing it to the Node.js runtime. This approach essentially abstracts away the typical transpilation step, making it transparent to the user.&lt;/p&gt;

&lt;h3 id=&quot;should-you-use-tsm&quot;&gt;Should you use tsm?&lt;/h3&gt;

&lt;p&gt;The primary and most obvious advantage to using tsm is that developers don’t need to worry about TypeScript transpilation. This means that development feedback loops are shorter, build tooling is simpler, and deployment is more straightforward. This is particularly convenient for small projects like one-off scripts.&lt;/p&gt;

&lt;p&gt;Because tsm runs on top of Node.js, developers familiar with Node will find using tsm natural and easy to incorporate TypeScript into their projects with a simple step.&lt;/p&gt;

&lt;p&gt;For example, esbuild has built-in source map support, which means that TypeScript programs that run under tsm can be debugged with the standard V8 debugging protocol (included with Node.js) and popular &lt;a href=&quot;https://blog.logrocket.com/how-to-debug-node-js-apps-in-visual-studio-code/&quot;&gt;debugging tools like VS Code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, the vast ecosystem of npm packages and Node built-in APIs are available to TypeScript running with tsm.&lt;/p&gt;

&lt;h3 id=&quot;the-disadvantages-to-tsm&quot;&gt;The disadvantages to tsm&lt;/h3&gt;

&lt;p&gt;One relatively small trade-off of using tsm is that it must be installed, in addition to Node.js itself, on the system that needs to run the TypeScript code.&lt;/p&gt;

&lt;p&gt;This can be an inconvenience if distributing scripts meant to be run directly from the shell (like when using tsm as the interpreter for a script via shebang interpreter directive), but not a problem for fully packaged Node.js modules.&lt;/p&gt;

&lt;p&gt;Other than that, there aren’t too many downsides to giving tsm a try, assuming there’s an appetite for new and emerging (and therefore, potentially changing) tech.&lt;/p&gt;

&lt;p&gt;Unlike other elements of a project’s technology stack (such as databases, programming languages, frameworks, etc.), the nice thing about JavaScript build tools is that they can be swapped out later with minimal impact on the application’s source code itself.&lt;/p&gt;

&lt;h3 id=&quot;using-tsm&quot;&gt;Using tsm&lt;/h3&gt;

&lt;p&gt;There are a few different ways to hook tsm into a Node.js project.&lt;/p&gt;

&lt;h4 id=&quot;1-as-a-replacement-for-the-node-executable&quot;&gt;1. As a replacement for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; executable&lt;/h4&gt;

&lt;p&gt;Assuming you have tsm installed globally (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm --global install tsm&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn global add tsm&lt;/code&gt;), you can run TypeScript files using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsm&lt;/code&gt; executable, like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;tsm index.ts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;For local installations, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsm&lt;/code&gt; executable can be found at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./node_modules/.bin/tsm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that any command-line flags will be passed through to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt;, which is convenient for experimental features, debugging configuration, and the like.&lt;/p&gt;

&lt;h4 id=&quot;2-with-a-shebang-interpreter-directive&quot;&gt;2. With a shebang interpreter directive&lt;/h4&gt;

&lt;p&gt;Because tsm includes an executable, it can be used in a shebang directive like any other script interpreter. Shebang directives are formatted as such: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!#&amp;lt;path/to/interpreter&amp;gt; [arguments]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;They appear at the first line of the file and tell the operating system which program to use to parse and execute the rest of the file.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#!/usr/local/bin/tsm
&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;hello from TypeScript!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;3-using-the---require-or---loader-hooks&quot;&gt;3. Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--require&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--loader&lt;/code&gt; hooks&lt;/h4&gt;

&lt;p&gt;If you still need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; directly rather than the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsm&lt;/code&gt; executable, you can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--require&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--loader&lt;/code&gt; hooks to instruct &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsm&lt;/code&gt; to load TypeScript files. tsm needs to be installed as a local dependency (rather than globally) for this to work.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;node &lt;span class=&quot;nt&quot;&gt;--require&lt;/span&gt; tsm index.ts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;node &lt;span class=&quot;nt&quot;&gt;--loader&lt;/span&gt; tsm index.ts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/using-tsm-with-require-hook.gif&quot; alt=&quot;Demo: using tsm with require hook&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;deno-an-alternative-to-tsm&quot;&gt;Deno: An alternative to tsm&lt;/h2&gt;

&lt;p&gt;tsm isn’t the only way to achieve transpilation-less executable TypeScript. Ryan Dahl, the creator of Node.js, has released a project called &lt;a href=&quot;https://deno.land/&quot;&gt;Deno&lt;/a&gt; that ships with TypeScript support out of the box.&lt;/p&gt;

&lt;p&gt;Like Node.js, Deno is built on top of the V8 JavaScript engine and seeks to overcome some of Node.js’s design flaws.&lt;/p&gt;

&lt;p&gt;Like Node.js and tsm, Deno can be used directly as an executable:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;deno index.ts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It can also be used in a shebang directive:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;usr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;deno&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;However, code written for Node.js in TypeScript may not be portable to Deno, as Deno provides its own standard library as a replacement for the Node.js APIs. It also has its own paradigm for distributing modules rather than utilizing npm. Check out &lt;a href=&quot;https://deno.land/std&quot;&gt;Deno’s standard library&lt;/a&gt; and &lt;a href=&quot;https://doc.deno.land/builtin/stable&quot;&gt;API docs&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;tsm is a great way to integrate TypeScript into a Node.js project quickly and seamlessly. Because it’s built upon Node.js, tsm users can leverage the vast ecosystem of packages in npm and the powerful Node.js APIs, all from within TypeScript without needing to set up a complicated build process.&lt;/p&gt;

&lt;p&gt;Check out tsm’s &lt;a href=&quot;https://github.com/lukeed/tsm&quot;&gt;GitHub repo&lt;/a&gt; and &lt;a href=&quot;https://github.com/lukeed/tsm/network/dependents?package_id=UGFja2FnZS00OTM3NTU4MzY%3D&quot;&gt;its dependents list&lt;/a&gt; to see what people are using it for. Some notable examples include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/yuanqing/create-figma-plugin&quot;&gt;create-figma-plugin&lt;/a&gt;: a comprehensive toolkit for developing plugins and widgets for Figma and FigJam. It is written in TypeScript and uses tsm for its development process&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/tinyhttp/tinyhttp&quot;&gt;tinyhttp&lt;/a&gt;: a modern web framework written in TypeScript. It uses tsm in its documentation examples as a simple way to get users up and running quickly&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/nanostores/nanostores&quot;&gt;nanostores&lt;/a&gt;: a tiny state manager for JavaScript projects. tsm is used in the development cycle for this module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, use tsm when you want to leverage Node.js’ vast ecosystem from within TypeScript or integrate TypeScript into an existing Node codebase. If you don’t have many npm-only dependencies and want to enjoy the benefits of Deno’s security model, consider using Deno for your next server-side TypeScript project.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;
</content:encoded>
      <pubDate>Mon, 24 Jan 2022 14:52:48 +0000</pubDate>
      <guid>https://mjswensen.com/blog/using-tsm-as-a-shebang-interpreter-in-typescript/</guid>
    </item>
    
    <item>
      <title>Using SVG vs. Canvas: A short guide</title>
      <link>https://mjswensen.com/blog/using-svg-vs-canvas/</link>
      <description>Canvas and SVG are both technologies that allow web developers to draw images onto the screen, but they have very different APIs and approaches to doing so.</description>
      <content:encoded>&lt;p&gt;So, you need to add some charts to your app or site? By now you’ve figured out that there are many different ways to accomplish this, most of which utilize one of two primary technologies: SVG and the Canvas API.&lt;/p&gt;

&lt;p&gt;In order to decide which will best fit your needs, we need to look at SVG’s and Canvas’ relative strengths and weaknesses and how those play into the different chart-drawing solutions available.&lt;/p&gt;

&lt;p&gt;Canvas and SVG are both technologies that allow web developers to draw images onto the screen, but they have very different APIs and approaches to doing so.&lt;/p&gt;

&lt;h2 id=&quot;what-is-svg&quot;&gt;What is SVG?&lt;/h2&gt;

&lt;p&gt;SVG stands for “scalable vector graphics” and represents a body of standards for declaratively defining the properties of an image (things like the shapes themselves, fill color, stroke color, and the like).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-svg&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg width=&quot;100&quot; height=&quot;100&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;
  &amp;lt;circle cx=&quot;50&quot; cy=&quot;50&quot; r=&quot;45&quot; fill=&quot;#FFA69E&quot; /&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/simple-svg-circle-result.png&quot; alt=&quot;Simple SVG circle result&quot; /&gt;&lt;/p&gt;

&lt;p&gt;SVG images are created either inline with HTML code by adding an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag, or can be defined in separate files (with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.svg&lt;/code&gt; extension) and referenced in the HTML.&lt;/p&gt;

&lt;h2 id=&quot;what-is-canvas&quot;&gt;What is Canvas?&lt;/h2&gt;

&lt;p&gt;Canvas is a lower-level browser standard API that allows developers to imperatively “draw” directly onto the image by issuing a series of commands:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fillStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#FFA69E&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/simple-canvas-circle-result.png&quot; alt=&quot;Simple Canvas circle result&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Canvas images are created by adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element to the HTML and then manipulating that element via the JavaScript DOM API as shown above.&lt;/p&gt;

&lt;h2 id=&quot;svg-vs-canvas-api-differences&quot;&gt;SVG vs. Canvas: API differences&lt;/h2&gt;

&lt;p&gt;Because Canvas is a lower level API than SVG, it offers more flexibility than SVG in exchange for greater complexity. The limit of what can be drawn on a Canvas is only limited by how much code a developer is willing to write. Any SVG image can be drawn with the Canvas API with enough effort.&lt;/p&gt;

&lt;p&gt;Conversely, because SVG is a higher level API compared to Canvas, it can create complex images without requiring developers to write complicated code.&lt;/p&gt;

&lt;p&gt;In this example, we apply lighting and blur effects to a couple of polygons in just 15 lines of SVG code. The corresponding Canvas code would be much more complex to produce the same image:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-svg&quot; data-lang=&quot;svg&quot;&gt;&amp;lt;svg width=&quot;200&quot; height=&quot;200&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;
  &amp;lt;defs&amp;gt;
    &amp;lt;filter id=&quot;point-light&quot;&amp;gt;
      &amp;lt;feSpecularLighting result=&quot;light&quot; specularConstant=&quot;1&quot; specularExponent=&quot;150&quot; lighting-color=&quot;#DDFFF7&quot;&amp;gt;
        &amp;lt;fePointLight x=&quot;100&quot; y=&quot;100&quot; z=&quot;200&quot;/&amp;gt;
      &amp;lt;/feSpecularLighting&amp;gt;
      &amp;lt;feComposite in=&quot;SourceGraphic&quot; in2=&quot;light&quot; operator=&quot;lighter&quot; /&amp;gt;
    &amp;lt;/filter&amp;gt;
    &amp;lt;filter id=&quot;blur&quot;&amp;gt;
      &amp;lt;feGaussianBlur in=&quot;SourceGraphic&quot; stdDeviation=&quot;3&quot; /&amp;gt;
    &amp;lt;/filter&amp;gt;
  &amp;lt;/defs&amp;gt;
  &amp;lt;polygon points=&quot;190,10 10,55 10,190&quot; fill=&quot;#AA4465&quot; filter=&quot;url(#point-light)&quot;/&amp;gt;
  &amp;lt;polygon points=&quot;10,10 190,100 100,190 100,10&quot; fill=&quot;#FFA69E&quot; filter=&quot;url(#blur)&quot; /&amp;gt;
&amp;lt;/svg&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/svg-with-complex-effects.png&quot; alt=&quot;SVG with complex effects&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, because Canvas offers more flexibility, not every image that can be drawn on a Canvas can be precisely expressed with SVG syntax (short of using tiny SVG &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;rect&amp;gt;&lt;/code&gt; elements as “pixels”).&lt;/p&gt;

&lt;p&gt;This image would be somewhat more difficult to produce using SVG:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;channel1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channel2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scaledVariability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xFF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channel1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;channel2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;scaledVariability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;scaledVariability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0xAA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x44&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x86&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;0x57&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;height&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;color2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proportion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;variability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fillStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`rgba(&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fillRect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/noisy-gradient-canvas.png&quot; alt=&quot;Noisy gradient drawn with Canvas commands&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;important-implications-to-consider-when-using-svg-vs-canvas&quot;&gt;Important implications to consider when using SVG vs. Canvas&lt;/h2&gt;

&lt;p&gt;The fundamental differences in API between SVG and Canvas outlined above have downstream effects that you should consider, such as the following.&lt;/p&gt;

&lt;h3 id=&quot;svg-elements-can-exist-in-the-dom-alongside-the-html-markup&quot;&gt;SVG elements can exist in the DOM alongside the HTML markup&lt;/h3&gt;

&lt;p&gt;This means that SVG DOM elements can be manipulated in ways that are familiar to web developers, including being integrated into popular frameworks like React or Vue.&lt;/p&gt;

&lt;p&gt;One downside of this is that complex SVG charts containing tens of thousands of nodes, for example, could prove to be too resource-intensive for browsers to render efficiently, leading to sluggish or janky web pages. This is because Canvas operations are committed directly to the screen, so the number of drawing operations is less impactful to performance in that there is no overhead of keeping track of these operations in the DOM.&lt;/p&gt;

&lt;h3 id=&quot;as-with-html-svg-is-declarative&quot;&gt;As with HTML, SVG is declarative&lt;/h3&gt;

&lt;p&gt;This means that the browser takes care of translating the desired image into the low-level imperative code for drawing pixels onto the screen — the developer doesn’t have to worry about that. Complex images can be succinctly represented in SVG syntax.&lt;/p&gt;

&lt;h3 id=&quot;svg-images-can-be-responsive-and-canvas-images-are-not&quot;&gt;SVG images can be responsive, and Canvas images are not&lt;/h3&gt;

&lt;p&gt;SVGs can grow and shrink in response to parent container sizes or screen size changes, whereas Canvas elements need custom JavaScript to achieve this behavior.&lt;/p&gt;

&lt;h2 id=&quot;svg-vs-canvas-which-is-better-for-you&quot;&gt;SVG vs. Canvas: Which is better for you?&lt;/h2&gt;

&lt;p&gt;With the differences between SVG and Canvas in mind, we can evaluate the needs of our chart project and determine which technology might better serve us. It’s worth asking the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Will the charts have many elements or be highly detailed? Canvas may perform better for showing this data without bogging down the browser with many DOM nodes. But if the charts are relatively simple, the SVG syntax may be a simpler and quicker solution to getting a nice-looking chart out the door quickly.&lt;/li&gt;
  &lt;li&gt;Do your charts need to have several animations or be responsive in width? While it’s certainly possible to render animations using Canvas or to make Canvas elements respond to width changes, it may be more straightforward to achieve these effects using SVG and CSS.&lt;/li&gt;
  &lt;li&gt;Do the charts need to be interactive? Because SVG elements are in the DOM, responding to user actions like clicks on particular elements of a chart is as simple as responding to events on any other DOM element. Canvas interactivity is possible but will require some additional code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;charting-libraries&quot;&gt;Charting libraries&lt;/h1&gt;

&lt;p&gt;Because rendering data visualizations is such a common use case for websites and applications, there are a plethora of libraries that make drawing charts with Canvas or SVG easy. A quick DuckDuckGo search of &lt;a href=&quot;https://duckduckgo.com/?q=JavaScript+chart+libraries&quot;&gt;“JavaScript chart libraries”&lt;/a&gt; will bring up dozens of pages outlining many of the different options available.&lt;/p&gt;

&lt;p&gt;Some popular options offer great-looking charts out of the box, such as &lt;a href=&quot;https://www.chartjs.org/&quot;&gt;Chart.js&lt;/a&gt; or &lt;a href=&quot;https://gionkunz.github.io/chartist-js/&quot;&gt;Chartist&lt;/a&gt;. Others are more complex but allow for more customization, like &lt;a href=&quot;https://vega.github.io/vega-lite/&quot;&gt;Vega-Lite&lt;/a&gt;. Many libraries are built upon the de-facto library for data visualization on the web, &lt;a href=&quot;https://d3js.org/&quot;&gt;D3.js&lt;/a&gt;, which offers complete flexibility and control at the expense of a steep learning curve.&lt;/p&gt;

&lt;h1 id=&quot;rendering-charts-on-the-backend&quot;&gt;Rendering charts on the backend&lt;/h1&gt;

&lt;p&gt;Creating charts with SVG and Canvas isn’t limited to browser contexts; they can be rendered in server applications as well.&lt;/p&gt;

&lt;p&gt;For SVG, a tool like &lt;a href=&quot;https://developers.google.com/web/tools/puppeteer/&quot;&gt;Puppeteer&lt;/a&gt; can be used to render SVG images in a headless Chrome browser and generate screenshots as desired. For Canvas, there is a fantastic library called &lt;a href=&quot;https://www.npmjs.com/package/canvas&quot;&gt;Canvas&lt;/a&gt; that provides a near-complete implementation of the standard Canvas API for Node.js.&lt;/p&gt;

&lt;h2 id=&quot;an-example-of-a-backend-canvas-rendering&quot;&gt;An example of a backend Canvas rendering&lt;/h2&gt;

&lt;p&gt;This JavaScript snippet encodes a secret message. To see what it is, save this code in a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createWriteStream&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createCanvas&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createCanvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2d&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strokeStyle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#861657&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineWidth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lineCap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;beginPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;160&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stroke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;message.png&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createWriteStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Canvas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;createPNGStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Image file created at &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then save a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; right next to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;canvas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^2.7.0&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;From the same directory where you saved &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;, run the following command (Node.js must installed available on your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt;):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;npm &lt;span class=&quot;nb&quot;&gt;install
&lt;/span&gt;node index.js&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If everything went according to plan, your headlessly generated PNG image with secret message will be saved to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message.png&lt;/code&gt;. 👋&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;SVG and Canvas are both powerful and mature technologies, each with a strong ecosystem of libraries and documentation to help bring your charts to life. And some libraries even support rendering to both SVG or Canvas!&lt;/p&gt;

&lt;p&gt;Don’t be afraid to invest some time in evaluating alternatives to find the right solution for your specific project’s needs. Here are some quick rules of thumb when it comes to deciding on SVG or Canvas.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Do your charts need to be responsive or interactive? SVG may offer these features more simply than Canvas.&lt;/li&gt;
  &lt;li&gt;Will your charts be highly detailed or complex? Canvas may offer better performance and greater control than SVG.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good luck!&lt;/p&gt;
</content:encoded>
      <pubDate>Mon, 31 May 2021 12:21:29 +0000</pubDate>
      <guid>https://mjswensen.com/blog/using-svg-vs-canvas/</guid>
    </item>
    
    <item>
      <title>What's new in TypeScript 4.2?</title>
      <link>https://mjswensen.com/blog/whats-new-in-typescript-4-2/</link>
      <description>Version 4.2 of TypeScript was released on 23 February, 2021, and with it a number of nice features, bug fixes, and performance improvements. Here are the most important ones you should be aware of.</description>
      <content:encoded>&lt;p&gt;Version 4.2 of TypeScript was released on 23 February, 2021, and with it a number of nice features, bug fixes, and performance improvements. Here are the most important ones you should be aware of.&lt;/p&gt;

&lt;h1 id=&quot;typescript-42s-top-enhancements&quot;&gt;TypeScript 4.2’s top enhancements&lt;/h1&gt;

&lt;h2 id=&quot;more-flexible-rest-elements-in-tuple-types&quot;&gt;More flexible rest elements in tuple types&lt;/h2&gt;

&lt;p&gt;Previous to 4.2, rest elements could only appear at the end of the tuple type. If we were modeling the state of a two-player game, for example, we might use this tuple type to hold our two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Player&lt;/code&gt;s and a list of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Move&lt;/code&gt;s they’ve played:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gameState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With 4.2, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Move&lt;/code&gt;s don’t have to be at the end of the type; they could be in the middle or even at the beginning. If desired, we can now expand our type as follows, to include which player’s turn it is. For additional clarity, we’ll take advantage of TypeScript’s support for labeled tuple elements (which is not new in 4.2):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;gameState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;player1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;player2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Player&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;moves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;currentTurn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;increased-visibility-into-which-files-are-included-in-your-compiled-program&quot;&gt;Increased visibility into which files are included in your compiled program&lt;/h2&gt;

&lt;p&gt;TypeScript’s compiler (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsc&lt;/code&gt;) now includes a new flag, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--explainFiles&lt;/code&gt;, that outputs a list of files included in the compilation as well as basic reasoning behind why they’re there, in a simple text format. This can be very helpful when developing or fine-tuning compiler configuration in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This feature is a nice first step toward debugging build time issues, and I expect to grow more robust and powerful in future releases (for example, a JSON output format for ingestion into other tools for more advanced analysis).&lt;/p&gt;

&lt;p&gt;Given a default TypeScript config and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.ts&lt;/code&gt; file with a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log(&#39;hello, world!&#39;);&lt;/code&gt;, here is some example output from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--explainFiles&lt;/code&gt; flag:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plain&quot; data-lang=&quot;plain&quot;&gt;node_modules/typescript/lib/lib.d.ts
  Default library
node_modules/typescript/lib/lib.es5.d.ts
  Library referenced via &#39;es5&#39; from file &#39;node_modules/typescript/lib/lib.d.ts&#39;
node_modules/typescript/lib/lib.dom.d.ts
  Library referenced via &#39;dom&#39; from file &#39;node_modules/typescript/lib/lib.d.ts&#39;
node_modules/typescript/lib/lib.webworker.importscripts.d.ts
  Library referenced via &#39;webworker.importscripts&#39; from file &#39;node_modules/typescript/lib/lib.d.ts&#39;
node_modules/typescript/lib/lib.scripthost.d.ts
  Library referenced via &#39;scripthost&#39; from file &#39;node_modules/typescript/lib/lib.d.ts&#39;
index.ts
  Root file specified for compilation&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Try it yourself using &lt;a href=&quot;https://github.com/mjswensen/typescript-4.2&quot;&gt;this example repository (complete with devcontainer.json) on GitHub.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;better-support-for-unused-destructured-variables&quot;&gt;Better support for unused destructured variables&lt;/h2&gt;

&lt;p&gt;When destructuring tuples or arrays, there are times when the elements you are interested in don’t appear at the beginning of the list. In these cases, “throwaway” variable names such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c&lt;/code&gt;, etc., are used for the elements of no interest.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noUnusedLocals&lt;/code&gt; compiler option on, however, these unused local variables would cause TypeScript to throw an error until version 4.2.&lt;/p&gt;

&lt;p&gt;Now, simply prepend the unused variable names with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; and TypeScript will happily ignore these variables and will not throw an error if they are unused. As an example, this new feature would be particularly useful when extracting bits of data from the rows of a CSV spreadsheet:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getCsvRows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getCsvRows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Destructure row, utilizing only the 1st, 4th, and 6th columns.&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;country&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ... do something with id, name, and country&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Prefixing unused variable names with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt; is a common convention in situations like these; this is an example of tool authors building thoughtfully around and supporting the existing behavior of their users.&lt;/p&gt;

&lt;h2 id=&quot;smarter-type-system-and-performance-improvements&quot;&gt;Smarter type system and performance improvements&lt;/h2&gt;

&lt;p&gt;As with any TypeScript release, there were also a number of smaller enhancements that are not groundbreaking on their own but they make TypeScript incrementally better and more comfortable to use. To name just a few:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A helpful error when trying to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;in&lt;/code&gt; operator on a primitive type. This is normally a runtime error (in JavaScript) but is now caught at compile time in TypeScript&lt;/li&gt;
  &lt;li&gt;An internal limit on tuple size in conjunction with the spread syntax, to improve compilation performance&lt;/li&gt;
  &lt;li&gt;Better parsing and interpretation of vanilla JavaScript files&lt;/li&gt;
  &lt;li&gt;A new flag called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--noPropertyAccessFromIndexSignature&lt;/code&gt; that can help reduce errors from object property name misspellings under certain situations&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;more-details-and-resources&quot;&gt;More details and resources&lt;/h1&gt;

&lt;p&gt;A full list of all the enhancements can be seen on the &lt;a href=&quot;https://github.com/microsoft/TypeScript/releases&quot;&gt;TypeScript project’s releases page on GitHub&lt;/a&gt;, as well as the &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-2&quot;&gt;release announcement on the TypeScript blog&lt;/a&gt;. Those are the top highlights of the TypeScript 4.2 release. To dive deeper in to the changes or learn more about TypeScript generally, check out the following resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/&quot;&gt;Release announcement&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript/releases/tag/v4.2.2&quot;&gt;4.2 release on GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript/compare/v4.1.5...v4.2.2&quot;&gt;TypeScript source code changes between versions 4.1.5 and 4.2.2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/play?ts=4.2.2&quot;&gt;TypeScript’s in-browser playground with version 4.2.2 pre-loaded&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript’s homepage, typescriptlang.org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook&quot;&gt;TypeScript documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
      <pubDate>Fri, 07 May 2021 12:09:43 +0000</pubDate>
      <guid>https://mjswensen.com/blog/whats-new-in-typescript-4-2/</guid>
    </item>
    
    <item>
      <title>The case for using frameworks</title>
      <link>https://mjswensen.com/blog/the-case-for-using-frameworks/</link>
      <description>Like any other tool, the stackless approach can be useful philosophy when approaching a problem. But when working on a team, or on a project with even moderate complexity, leveraging modern frameworks will likely yield dividends throughout the life of the project.</description>
      <content:encoded>&lt;p&gt;Complaints against the complexity of modern web development tooling setups have been ever increasing in recent years as the web platform continues to evolve and innovate. A recent iteration of this movement toward simplicity is called the “stackless” (or “framework-less”) approach, coined by &lt;a href=&quot;https://tutorials.yax.com/&quot;&gt;Daniel Keyhoe of yax.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core tenets of the stackless approach are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Lean into natively supported features and standards of the web platform&lt;/li&gt;
  &lt;li&gt;Avoid build tooling and frameworks by using CDN-served npm packages instead&lt;/li&gt;
  &lt;li&gt;Use web components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core sentiment of utilizing standards is an admirable one, and indeed it may suffice for very small projects or prototypes, but here are some reasons why this approach is infeasible in practice for professional web development or projects of material size.&lt;/p&gt;

&lt;h1 id=&quot;frameworks-will-never-go-away&quot;&gt;Frameworks will never go away&lt;/h1&gt;

&lt;p&gt;Frameworks play a critical role in the frontend ecosystem. As the web platform advances, so do the frameworks that build on top of it. They serve as a critical space to innovate and inform the future direction of the web standards.&lt;/p&gt;

&lt;p&gt;Frameworks are built to overcome the deficiencies of the standard features until new standard features are introduced to fill the gaps. They can move faster than standards do, and can experiment with features to fine-tune and validate them before they get proposed and adopted by the platform. An example of this was the revolutionary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jQuery&lt;/code&gt; function (commonly called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt;) in the 2000s and 2010s that directly informed what we know today as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.querySelectorAll&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;stackless-has-too-many-limitations&quot;&gt;Stackless has too many limitations&lt;/h1&gt;

&lt;p&gt;To Keyhoe’s credit, the limitations of the stackless approach are clearly acknowledged in &lt;a href=&quot;https://tutorials.yax.com/articles/the-yax-way/index.html&quot;&gt;the introductory tutorials&lt;/a&gt;. Unfortunately, they are prohibitively severe for many types of modern web development projects.&lt;/p&gt;

&lt;h2 id=&quot;data-persistence-is-ignored&quot;&gt;Data persistence is ignored&lt;/h2&gt;

&lt;p&gt;Perhaps the largest omission in the stackless approach is that it doesn’t account for data persistence. The approach talks about abandoning Rails for standards-based JavaScript, HTML, and CSS, but Rails solves a different problem than the frontend browser technologies.&lt;/p&gt;

&lt;p&gt;What about an API to hide sensitive or proprietary business logic from the frontend? What about centralized state persistence? What about user authentication? There are so many common problems and paradigms that the stackless approach simply doesn’t account for.&lt;/p&gt;

&lt;p&gt;Using “functions” (meaning serverless or Lambda functions) is mentioned in passing, but if you are using serverless functions backed by a database or other storage, all of a sudden you have a “stack” again.&lt;/p&gt;

&lt;h2 id=&quot;css-is-too-low-level-to-author-maintainability-without-a-framework&quot;&gt;CSS is too low-level to author maintainability without a framework&lt;/h2&gt;

&lt;p&gt;CSS is powerful and chock full of features, but in favor of flexibility, it lacks adequate constraints to guide engineers toward solutions that are maintainable or easy for team members to contribute to.&lt;/p&gt;

&lt;p&gt;Even with powerful tools like CSS preprocessors, the topic of keeping CSS under control and well-documented is a common point of discussion for software teams. Even the stackless approach &lt;a href=&quot;https://tutorials.yax.com/articles/the-yax-way/2.html&quot;&gt;recommends using a CSS framework in the fine print.&lt;/a&gt; This is akin to titling an article “How to tour America on foot,” and then in the body paragraph write, “Until we figure out a way to walk faster, just use a car.”&lt;/p&gt;

&lt;h2 id=&quot;state-management-is-unaccounted-for&quot;&gt;State management is unaccounted for&lt;/h2&gt;

&lt;p&gt;Web components are ill-equipped to manage the complex state required to build an advanced frontend application. The stackless approach can work for simple informational sites, but for applications with complex user interfaces, the state management becomes unwieldy without the help of a framework or state management library.&lt;/p&gt;

&lt;p&gt;This is the second time the answer to “how to build an app without a framework” is “actually, just use a framework.” There are very few classes of applications that are both useful and don’t require the management of complex state.&lt;/p&gt;

&lt;h2 id=&quot;complexity-is-not-addressed&quot;&gt;Complexity is not addressed&lt;/h2&gt;

&lt;p&gt;The core problem with the stackless approach is that it tries to abandon complexity without a proposal on where that complexity should go. Complexity, in general, cannot be eliminated — it can only be pushed around to different parts of the system. Here are a few examples:&lt;/p&gt;

&lt;h3 id=&quot;hypermedia-apis-vs-bespoke-restful-apis&quot;&gt;Hypermedia APIs vs bespoke RESTful APIs&lt;/h3&gt;

&lt;p&gt;Hypermedia APIs return the relevant data for the resource, along with links to related API resources that the client must subsequently request. The complexity of assembling the full picture is pushed to the client.&lt;/p&gt;

&lt;p&gt;A bespoke API, on the other hand, may handle the complexity of gathering all of the relevant data before returning it to the client in a single, neat package. The complexity of assembling the requisite data can be moved around but not eliminated.&lt;/p&gt;

&lt;h3 id=&quot;create-react-app-and-react-scripts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-react-app&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-scripts&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The Facebook team behind &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; has tried to abstract away the complexity of maintaining &lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack&lt;/a&gt; and other configuration to bootstrap a React application as quickly as possible. This allows engineers to create fully functional React application without having to worry about installing and configuring all the tooling; they only have to install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-scripts&lt;/code&gt;  package.&lt;/p&gt;

&lt;p&gt;The build tool complexity didn’t go away, its burden just moved into the application’s dependencies rather than on the shoulders of the engineer building the application.&lt;/p&gt;

&lt;h1 id=&quot;redeeming-qualities-of-the-stackless-way&quot;&gt;Redeeming qualities of the stackless way&lt;/h1&gt;

&lt;p&gt;Though using the stackless approach may not be feasible for projects larger than hobbies or prototypes, there are some excellent points in its philosophy that can benefit software projects regardless of which approach they use.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;“Users are the users.” The focus on users’ needs, where the definition of users is literally the end users of the software, is a critical point that developers should stay as close to as possible. It is only by keeping the users in mind that applications can be designed empathetically and with critical attributes such as performance and progressive enhancement at the forefront.&lt;/li&gt;
  &lt;li&gt;“Stick to standards.” One of the best features of the web is that it moves forward on open standards and an unfailing spirit of backward compatibility. Framework authors and framework users alike should embrace and utilize standard features as much as possible (without being afraid to use non-standard technology such as frameworks when it enables them to build better software more quickly).&lt;/li&gt;
  &lt;li&gt;“Embrace simplicity where possible.” The idea of using the best and simplest tool for the job is pervasive in the stackless approach, and indeed, is part of the core DNA of the web. It’s easy for engineers to fall into the trap of “over-engineering” solutions to problems that end up just creating more problems themselves; it takes discipline and the wisdom of experience to avoid this trap.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;use-stackless-where-it-makes-sense&quot;&gt;Use stackless where it makes sense&lt;/h1&gt;

&lt;p&gt;Like any other tool, the stackless approach can be useful philosophy when approaching a problem. If you need to build a simple app where the features of the raw web platform will suffice, go for it.&lt;/p&gt;

&lt;p&gt;Using the least powerful technology capable of accomplishing the task or solving the problem is an excellent way to build solutions.&lt;/p&gt;

&lt;p&gt;But when working on a team, or on a project with even moderate complexity, leveraging modern frameworks will likely yield dividends throughout the life of the project.&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 30 Apr 2021 11:58:08 +0000</pubDate>
      <guid>https://mjswensen.com/blog/the-case-for-using-frameworks/</guid>
    </item>
    
    <item>
      <title>How polymorphic JavaScript functions affect performance</title>
      <link>https://mjswensen.com/blog/how-polymorphic-javascript-functions-affect-performance/</link>
      <description>Polymorphic APIs can be convenient to use due to their flexibility. In certain situations, they can be more expensive to execute, as JavaScript engines cannot optimize them as aggressively as simpler, monomorphic functions.</description>
      <content:encoded>&lt;p&gt;As with any conversation about performance, we need to gain some shared context around the type of JavaScript code we want to optimize and the context in which it will run. So, let’s start with some definitions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance.&lt;/strong&gt; First of all, when we use the word performance in the context of a computer program, we are referring to how quickly or efficiently that program can execute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymorphic functions.&lt;/strong&gt; A polymorphic function is a function that changes its behavior based on the types of arguments that are passed to it.&lt;/p&gt;

&lt;p&gt;The key word here is &lt;em&gt;types,&lt;/em&gt; as opposed to &lt;em&gt;values.&lt;/em&gt; (A function that didn’t change its output based on different values for arguments would not be a very useful function at all.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript engine.&lt;/strong&gt; In order to think about performance productively, we also need to know where our JavaScript is going to be executed. For our example code, we’ll use the &lt;a href=&quot;https://v8.dev/&quot;&gt;V8 engine&lt;/a&gt; given its popularity.&lt;/p&gt;

&lt;p&gt;V8 is the engine that powers the Chrome browser, Node.js, the Edge browser, and more. Note that there are also other JavaScript engines with their own performance characteristics, such as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey&quot;&gt;SpiderMonkey&lt;/a&gt; (used by Firefox), &lt;a href=&quot;https://developer.apple.com/documentation/javascriptcore&quot;&gt;JavaScriptCore&lt;/a&gt; (used by Safari), and others.&lt;/p&gt;

&lt;h1 id=&quot;creating-a-polymorphic-function-in-javascript&quot;&gt;Creating a polymorphic function in JavaScript&lt;/h1&gt;

&lt;p&gt;Suppose we are building a JavaScript library that enables other engineers to easily store messages to an in-memory database with our simple API. In order to make our library as easy and comfortable to use as possible, we provide a single polymorphic function that is very flexible with the arguments that it receives.&lt;/p&gt;

&lt;h2 id=&quot;option-1-use-completely-separate-arguments&quot;&gt;Option 1: Use completely separate arguments&lt;/h2&gt;

&lt;p&gt;The first signature of our function will take the required data as three separate arguments, and can be called like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;option-2-use-message-contents-with-options-object&quot;&gt;Option 2: Use message contents with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt; object&lt;/h2&gt;

&lt;p&gt;This signature will allow consumers to separate the required data (message contents) from the optional data (the author and the timestamp) into two separate arguments. We’ll accept the arguments in any order, for convenience.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;option-3-use-an-options-object&quot;&gt;Option 3: Use an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt; object&lt;/h2&gt;

&lt;p&gt;We’ll also allow users of our API to call the function passing in a single argument of an object containing all of the data we need:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;option-4-use-only-the-message-contents&quot;&gt;Option 4: Use only the message contents&lt;/h2&gt;

&lt;p&gt;Finally, we’ll allow users of our API to provide only the message contents, and we’ll provide default values for the rest of the data:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;implementing-a-polymorphic-function&quot;&gt;Implementing a polymorphic function&lt;/h2&gt;

&lt;p&gt;OK, with our API defined, we can build the implementation of our polymorphic function.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// We&#39;ll utilize an array for a simple in-memory database.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Once we get our input into a unified format, we&#39;ll use this function to&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// store it on our database and calculate an identifier that represents the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// data.&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// If the developer has passed us all the data individually, we&#39;ll package&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// it up into an object and store it in the database.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Or, if the developer has provided a message string and an options object,&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// we&#39;ll figure out which order they came in and then save appropriately.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Otherwise, we&#39;ve either gotten a string message or a complete set of&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// options.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// If the single argument is the string message, save it to the database&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// with some default values for author and timestamp.&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Anonymous&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// Otherwise, just save the options object in the database as-is.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;OK, now we’ll write some code that stores a lot of messages using our function — taking advantage of its polymorphic API — and measure its performance.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;perf_hooks&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Batman&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Why do we fall? So we can learn to pick ourselves back up.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Life doesn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;t give us purpose. We give life purpose.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;The Flash&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;No matter how bad things get, something good is out there, over the horizon.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Uncle Ben&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;With great power comes great responsibility.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Ms. Marvel&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;When you decide not to be afraid, you can find friends in super unexpected places.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Better late than never, but never late is better.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Inserted &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; records into the database.`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Duration: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; milliseconds`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now let’s implement our function again but with a simpler, monomorphic API.&lt;/p&gt;

&lt;h1 id=&quot;creating-a-monomorphic-function-in-javascript&quot;&gt;Creating a monomorphic function in JavaScript&lt;/h1&gt;

&lt;p&gt;In exchange for a more restrictive API, we can trim down the complexity of our function and make it monomorphic, meaning that the arguments of the function are always of the same type and in the same order.&lt;/p&gt;

&lt;p&gt;Although it won’t be as flexible, we can keep some of the ergonomics of the previous implementation by utilizing &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters&quot;&gt;default arguments&lt;/a&gt;. Our new function will look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// We&#39;ll again utilize an array for a simple in-memory database.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Rather than a generic list of arguments, we&#39;ll take the message contents and&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// optionally the author and timestamp.&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Anonymous&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// First we&#39;ll save our record into our database array.&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timestamp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// As before, we&#39;ll calculate and return an identifier that represents the&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// data, but we&#39;ll inline the contents of the function since there&#39;s no need&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// to re-use it.&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We’ll update the performance measuring code from our previous example to use our new unified API.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;perf_hooks&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Why do we fall? So we can learn to pick ourselves back up.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Batman&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Life doesn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;t give us purpose. We give life purpose.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;The Flash&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;No matter how bad things get, something good is out there, over the horizon.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;With great power comes great responsibility.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Uncle Ben&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;When you decide not to be afraid, you can find friends in super unexpected places.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Ms. Marvel&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;saveMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Better late than never, but never late is better.&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Inserted &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; records into the database.`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Duration: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; milliseconds`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;results&quot;&gt;Results&lt;/h1&gt;

&lt;p&gt;OK, now let’s run our programs and compare the results.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;node polymorphic.js
Inserted 30000 records into the database.
Duration: 6565.41 milliseconds

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;node monomorphic.js
Inserted 30000 records into the database.
Duration: 2955.01 milliseconds&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The monomorphic version of our function is about twice as fast as the polymorphic version, as there is less code to execute in the monomorphic version. But because the types and shapes of the arguments in the polymorphic version vary widely, V8 has a more difficult time making optimizations to our code.&lt;/p&gt;

&lt;p&gt;In simple terms, when V8 can identify (a) that we call a function frequently, and (b) that the function gets called with the same types of arguments, V8 can create “shortcuts” for things like object property lookups, arithmetic, string operations, and more.&lt;/p&gt;

&lt;p&gt;For a deeper look at how these “shortcuts” work I would recommend this article: &lt;a href=&quot;https://mrale.ph/blog/2015/01/11/whats-up-with-monomorphism.html&quot;&gt;&lt;em&gt;What’s up with monomorphism?&lt;/em&gt; by Vyacheslav Egorov&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;pros-and-cons-of-polymorphic-vs-monomorphic-functions&quot;&gt;Pros and cons of polymorphic vs monomorphic functions&lt;/h1&gt;

&lt;p&gt;Before you go off optimizing all of your code to be monomorphic, there are a few important points to consider first:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Polymorphic function calls are not likely to be your performance bottleneck.&lt;/strong&gt; There are many other types of operations that contribute much more commonly to performance problems, like latent network calls, moving large amounts of data around in memory, disk i/o, complex database queries, to name just a few.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You will only run into performance issues with polymorphic functions if those functions are very, very “hot” (frequently run).&lt;/strong&gt; Only highly specialized applications, similar to our contrived examples above, will benefit from optimization at this level. If you have a polymorphic function that runs only a few times, there will be no benefit from rewriting it to be monomorphic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You will have more luck updating your code to be efficient rather than trying to optimize for the JavaScript engine.&lt;/strong&gt; In most cases, applying good software design principles and paying attention to the complexity of your code will take you further than focusing on the underlying runtime. Also, V8 and other engines are constantly getting faster, so some performance optimizations that work today may become irrelevant in a future version of the engine.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Polymorphic APIs can be convenient to use due to their flexibility. In certain situations, they can be more expensive to execute, as JavaScript engines cannot optimize them as aggressively as simpler, monomorphic functions.&lt;/p&gt;

&lt;p&gt;In many cases, however, the difference will be insignificant. API patterns should be based on other factors like legibility, consistency, and maintainability because performance issues are more likely to crop up in other areas anyway. Happy coding!&lt;/p&gt;
</content:encoded>
      <pubDate>Tue, 30 Mar 2021 13:32:23 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-polymorphic-javascript-functions-affect-performance/</guid>
    </item>
    
    <item>
      <title>Creating compound components in React and Angular</title>
      <link>https://mjswensen.com/blog/creating-compound-components-in-react-and-angular/</link>
      <description>There are many different approaches to authoring components. In this post, we'll talk about the compound component pattern and when it may be an appropriate choice for component authors.</description>
      <content:encoded>&lt;h1 id=&quot;creating-compound-components-in-react-and-angular&quot;&gt;Creating compound components in React and Angular&lt;/h1&gt;

&lt;p&gt;There’s no question that components are a powerful abstraction for authoring frontend user interfaces (and, it turns out, &lt;a href=&quot;https://github.com/vadimdemedes/ink&quot;&gt;text-based UIs&lt;/a&gt;, &lt;a href=&quot;https://github.com/ReactTraining/react-router&quot;&gt;application routers&lt;/a&gt;, and many other types of interfaces).&lt;/p&gt;

&lt;p&gt;But there are many different approaches to authoring components. In this post, we’ll talk about the compound component pattern and when it may be an appropriate choice for component authors.&lt;/p&gt;

&lt;h1 id=&quot;what-are-compound-components&quot;&gt;What are compound components?&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.logrocket.com/guide-to-react-compound-components-9c4b3eb482e9/&quot;&gt;Compound components&lt;/a&gt; are groups of individual components that come together to create a cohesive and complex behavior. They work together, sharing state behind the scenes, to offer an intuitive and expressive syntax for the programmer who is using the components. One of the most familiar examples of compound components comes from vanilla HTML:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;select&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apples&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Apples&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;oranges&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Oranges&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;option&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;pears&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Pears&lt;span class=&quot;nt&quot;&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt; elements come together to form a complex input control that is familiar for many users. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;select&amp;gt;&lt;/code&gt; maintains its own internal state for which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt; is currently selected by the user and provides advanced behaviors like keyboard input to change the selected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;option&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;how-to-implement-compound-components&quot;&gt;How to implement compound components&lt;/h1&gt;

&lt;p&gt;First, make sure a compound component is the appropriate API design. Like any programming paradigm, compound components come with their fair share of tradeoffs. Generally, any pattern where there is implicit shared state — or behavior that is “magical” or not easily discoverable — can cause headaches down the road if not designed with utmost care.&lt;/p&gt;

&lt;p&gt;Some questions to ask yourself before designing a compound component are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Does the programmer using the component really need to compose two (or more) components together to achieve the desired result? Or can the same result be achieved with a simpler single-component design and appropriate inputs or props?&lt;/li&gt;
  &lt;li&gt;Will the interactions and roles between the components be clear and intuitive to programmers using them?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Carefully consider various APIs for your new component and consult with colleagues on which API they would prefer to consume.&lt;/p&gt;

&lt;p&gt;Here are some examples of common UI paradigms that lend well to the compound component pattern:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An advanced table component that allows users of the component to provide not only the tabular data but also custom sorting or filtering behavior&lt;/li&gt;
  &lt;li&gt;A searchable dropdown component where users of the component will be providing the options&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;compound-component-architecture-in-react&quot;&gt;Compound component architecture in React&lt;/h2&gt;

&lt;p&gt;To implement our example compound components in React, we will be leveraging a number of React APIs, so a foundational knowledge of each will be helpful:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot;&gt;Hooks&lt;/a&gt; and functional components&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/context.html&quot;&gt;Context API&lt;/a&gt; for sharing state between components&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;building-a-compound-component&quot;&gt;Building a compound component&lt;/h2&gt;

&lt;p&gt;We will build a rough draft of a compound component that allows the user to select multiple options from a list, using a text field to filter the list if desired.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/compound-component-demo.gif&quot; alt=&quot;Compound component demo&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;implementing-sub-components-and-an-app-component&quot;&gt;Implementing sub-components and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App&lt;/code&gt; component&lt;/h3&gt;

&lt;p&gt;To create our compound component, we will implement two sub-components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelect&lt;/code&gt;: this will be the outer wrapping component and will have the following roles:
    &lt;ul&gt;
      &lt;li&gt;Encapsulate the state of the child options&lt;/li&gt;
      &lt;li&gt;Render the text field for filtering options&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelectOption&lt;/code&gt;: this component will express the individual selectable options and will have the following roles:
    &lt;ul&gt;
      &lt;li&gt;Read from and write to the selection state based on user interaction&lt;/li&gt;
      &lt;li&gt;Read the filter value and exclude itself from rendering if appropriate&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we will also implement an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App&lt;/code&gt; component that uses our compound component, to develop and test its API.&lt;/p&gt;

&lt;h4 id=&quot;enhancedmultiselect&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelect&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;First, we’ll create a &lt;a href=&quot;https://reactjs.org/docs/context.html&quot;&gt;context&lt;/a&gt; that will be used to share state between the parent and child components.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next, we’ll implement the signature for our component, which will take three props:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;children&lt;/code&gt;: the children in the render tree, which will include the selectable options and any other markup required by the UI. We don’t care how deeply nested the options appear or if there are other components in the tree, allowing flexibility in usage for the engineer who is using our component&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt;: a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt; of strings representing the selected options&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onChange&lt;/code&gt;: a function that we will call with a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt; whenever the selection changes&lt;/p&gt;

    &lt;p&gt;export default function EnhancedMultiSelect({ children, value, onChange }) {}&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we’ll implement the body of our component. First, we’ll use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useState&lt;/code&gt; hook to keep track of the query the user has typed into the filter text input.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We’ll next return the components that React will render for us. We’ll first set up a provider for the context we set up earlier and use it to provide a few values that will be used by the options later on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isSelected&lt;/code&gt; function that takes a string key and returns whether or not the given key appears in the selection&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setSelected&lt;/code&gt; function that takes a key and adds or removes the key from the selection as indicated&lt;/li&gt;
  &lt;li&gt;The current value of the filter text input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll also render our filter text input and the components children inside the context provider. Here is the full source code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelect&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnhancedMultiSelect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onChange&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Provider&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;isSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;setSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;nf&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Filter options...&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setFilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;enhancedmultiselectoption&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelectOption&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Now we’ll implement the other half of our compound component, which will take two props:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;children&lt;/code&gt;: for displaying whatever the user of our component would like to render inside the selectable option&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt;: a string for representing this option; if this option is selected, this value will be included in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt; exposed by the parent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelect&lt;/code&gt; component&lt;/p&gt;

    &lt;p&gt;export default function EnhancedMultiSelectOption({ children, value }) {}&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first thing we’ll do in the body of our component is consume the context provided by the parent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelect&lt;/code&gt; component, using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot;&gt;destructuring assignment&lt;/a&gt; to pull the context apart for easier usage.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have the user’s filter query from the context, if it doesn’t match the option’s value, we’ll return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;null&lt;/code&gt; to render nothing:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, we’ll render the checkbox and plug its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checked&lt;/code&gt; state into our compound component’s selection state, as well as any children the consumer of our component would like to render. Here is the full source code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelectOption&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./EnhancedMultiSelect&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;isSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;evt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;app&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;To see how it all works together, we’ll consume our compound component and render it in an entry point &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;App&lt;/code&gt; component:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-jsx&quot; data-lang=&quot;jsx&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelect&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./EnhancedMultiSelect&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setSelection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelect&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setSelection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apples&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          Apples
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;oranges&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          Oranges
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;peaches&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          Peaches
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;grapes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          Grapes
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;plums&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
          Plums
        &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;compound-component-architecture-in-angular&quot;&gt;Compound component architecture in Angular&lt;/h2&gt;

&lt;p&gt;Let’s build the same simple compound component using the &lt;a href=&quot;https://angular.io/&quot;&gt;Angular&lt;/a&gt; framework.&lt;/p&gt;

&lt;h4 id=&quot;enhanced-multi-selectcomponentts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enhanced-multi-select.component.ts&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;For the outer component, we’ll set up a simple template that contains a text input with a two-way binding to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter&lt;/code&gt; property. Like in the React example, we’ll create an input for the selection state and an output when when the selection state changes. Here’s the full source code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EventEmitter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-enhanced-multi-select&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;input type=&quot;text&quot; [(ngModel)]=&quot;filter&quot; /&amp;gt;
    &amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;valueChange&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;enhanced-multi-select-optioncomponentts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enhanced-multi-select-option.component.ts&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;For the option items, we’ll render a label that wraps the checkbox and the content of the component, just like in the React example. We’ll utilize Angular’s &lt;a href=&quot;https://angular.io/guide/dependency-injection&quot;&gt;dependency injection&lt;/a&gt; system to get a reference to the parent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnhancedMultiSelectComponent&lt;/code&gt; instance passed via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constructor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that reference, we can evaluate and manipulate the state directly and check to see if the option should be visible according to the value of the user-provided filter string. Here is the source code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./enhanced-multi-select.component&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-enhanced-multi-select-option&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;label *ngIf=&quot;visible()&quot; style=&quot;display: block&quot;&amp;gt;
      &amp;lt;input
        type=&quot;checkbox&quot;
        [ngModel]=&quot;selected()&quot;
        (ngModelChange)=&quot;setSelected($event)&quot;
      /&amp;gt;
      &amp;lt;ng-content&amp;gt;&amp;lt;/ng-content&amp;gt;
    &amp;lt;/label&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EnhancedMultiSelectOptionComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;EnhancedMultiSelectComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;visible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;setSelected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;appcomponentts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.component.ts&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Finally, we’ll utilize our compound component and display the formatted JSON selection data for demonstration purposes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-typescript&quot; data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-root&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;app-enhanced-multi-select [(value)]=&quot;selection&quot;&amp;gt;
      &amp;lt;app-enhanced-multi-select-option value=&quot;apples&quot;&amp;gt;
        Apples
      &amp;lt;/app-enhanced-multi-select-option&amp;gt;
      &amp;lt;app-enhanced-multi-select-option value=&quot;oranges&quot;&amp;gt;
        Oranges
      &amp;lt;/app-enhanced-multi-select-option&amp;gt;
      &amp;lt;app-enhanced-multi-select-option value=&quot;peaches&quot;&amp;gt;
        Peaches
      &amp;lt;/app-enhanced-multi-select-option&amp;gt;
      &amp;lt;app-enhanced-multi-select-option value=&quot;grapes&quot;&amp;gt;
        Grapes
      &amp;lt;/app-enhanced-multi-select-option&amp;gt;
      &amp;lt;app-enhanced-multi-select-option value=&quot;plums&quot;&amp;gt;
        Plums
      &amp;lt;/app-enhanced-multi-select-option&amp;gt;
    &amp;lt;/app-enhanced-multi-select&amp;gt;
    &amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;{{ selectionArray() | json }}&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AppComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nf&quot;&gt;selectionArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[...&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;In this post we’ve implemented a filterable multi-select compound component in React, using the &lt;a href=&quot;https://reactjs.org/docs/context.html&quot;&gt;Context API&lt;/a&gt;, and in Angular, using &lt;a href=&quot;https://angular.io/guide/dependency-injection&quot;&gt;dependency injection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Compound components are one option for creating a simple API to compose behavior that is too complex for a single component. There are plenty of alternative patterns, such as &lt;a href=&quot;https://reactjs.org/docs/render-props.html&quot;&gt;“render props”&lt;/a&gt; in React, and each pattern’s trade-offs should be carefully considered for a particular use case.&lt;/p&gt;

&lt;p&gt;The full source code for running the above examples in a development environment can be found on &lt;a href=&quot;https://github.com/mjswensen/compound-components&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
      <pubDate>Mon, 15 Mar 2021 13:23:09 +0000</pubDate>
      <guid>https://mjswensen.com/blog/creating-compound-components-in-react-and-angular/</guid>
    </item>
    
    <item>
      <title>Should you drop support for IE 11?</title>
      <link>https://mjswensen.com/blog/should-you-drop-support-for-internet-explorer-11/</link>
      <description>Each app, business, and project will have a different answer and timeline for dropping legacy browser support. Here are some different aspects of this question as you consider dropping IE 11 for your app or web site.</description>
      <content:encoded>&lt;p&gt;Celebration. Joy. Relief.&lt;/p&gt;

&lt;p&gt;These are some of the words used to describe a web developer’s reaction to &lt;a href=&quot;https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666#ftag=MSF278e2c0&quot;&gt;Microsoft’s announcement that some of their own apps and services will end support for Internet Explorer 11 in August 2021&lt;/a&gt;. While IE 11 is &lt;a href=&quot;https://docs.microsoft.com/en-us/lifecycle/faq/internet-explorer-microsoft-edge#what-is-the-lifecycle-policy-for-internet-explorer&quot;&gt;not officially sunsetted&lt;/a&gt; (it follows the same support cycle as the platforms with which it is included), it has taken a back seat to its replacement, Microsoft Edge, and is definitively on its way out.&lt;/p&gt;

&lt;p&gt;And though precise measurement of browser usage is difficult, IE 11 &lt;a href=&quot;https://gs.statcounter.com/&quot;&gt;likely represents approximately just 1% of the internet’s browsing traffic&lt;/a&gt; at the time of writing.&lt;/p&gt;

&lt;p&gt;So, should your app or service continue to invest time in IE 11 support? The answer is every software engineer’s favorite: &lt;em&gt;it depends.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each app, business, and project will have a different answer and timeline for dropping legacy browser support. Below are some different aspects of this question as you consider dropping IE 11 for your app or web site.&lt;/p&gt;

&lt;h2 id=&quot;should-you-drop-ie-11-support&quot;&gt;Should you drop IE 11 support?&lt;/h2&gt;

&lt;h3 id=&quot;who-are-your-customers&quot;&gt;Who are your customers?&lt;/h3&gt;

&lt;p&gt;Identifying who your users are and how they interact with your web property is paramount to making decisions regarding browser support. Are your users primarily from large corporations whose IT departments have strict policies in place that limit choice for web browsing software, or are your customers from small businesses where the latest tools and technologies are the norm?&lt;/p&gt;

&lt;p&gt;How many of your current users interact with your app using IE 11? What job(s) does your software get done for your users? Answering these fundamental questions will lay the groundwork for a productive internal discussion on whether or not to drop IE 11 support.&lt;/p&gt;

&lt;h3 id=&quot;how-much-do-your-customers-pay-for-your-app-or-site&quot;&gt;How much do your customers pay for your app or site?&lt;/h3&gt;

&lt;p&gt;Do you build a B2B SaaS offering with large but infrequent contracts, or are you building a hobby app in your free time outside of a regular job?&lt;/p&gt;

&lt;p&gt;Is the technology itself the core value proposition of your app or does it simply enable your underlying business model?&lt;/p&gt;

&lt;p&gt;In each of these cases, a shift in legacy technology support will have dramatic differences in how the project or business will be impacted. You need to involve all key stakeholders of the app and carefully weigh the benefits of developer experience against any potential reductions in revenue or other business implications.&lt;/p&gt;

&lt;h3 id=&quot;does-your-company-have-a-service-level-agreement-in-place-that-outlines-which-technologies-you-are-obligated-to-support&quot;&gt;Does your company have a service level agreement in place that outlines which technologies you are obligated to support?&lt;/h3&gt;

&lt;p&gt;Some large contracts include stipulations about legacy support timelines and service level agreements for bug fixes, among other things. If applicable, be sure to check with your company’s legal department to be sure that dropping IE 11 support would not constitute a breach of any contract.&lt;/p&gt;

&lt;h3 id=&quot;do-the-benefits-outweigh-the-costs-for-dropping-ie-11-support&quot;&gt;Do the benefits outweigh the costs for dropping IE 11 support?&lt;/h3&gt;

&lt;p&gt;Ultimately, all of the above exploratory questions lead to a final cost-benefit analysis on dropping vs. maintaining IE 11 support for your site or app. Answers to the above questions will help you rank, compare, and add to the following lists of example costs and benefits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Costs of dropping IE 11 support&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Some users may no longer be able to use your site or app. This could decrease revenue or increase support ticket volume, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Benefits of dropping IE 11 support&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Your site may be able to take better advantage of modern APIs or browser features&lt;/li&gt;
  &lt;li&gt;You may be able to simplify your testing and quality assurance processes&lt;/li&gt;
  &lt;li&gt;Your development cycle may be faster due to decreased compatibility requirements&lt;/li&gt;
  &lt;li&gt;You may be able to reduce the amount of code that is downloaded for your site/app if polyfills for older browsers can be removed&lt;/li&gt;
  &lt;li&gt;If you were already shipping different bundles for different levels of browser support, your deployed assets may be able to be simplified or consolidated&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;how-to-pragmatically-drop-support-for-ie-11&quot;&gt;How to pragmatically drop support for IE 11&lt;/h3&gt;

&lt;p&gt;OK, so you’ve carefully considered all the possibilities and potential implications of dropping IE 11 support and have decided that the benefits outweigh the costs. What’s next?&lt;/p&gt;

&lt;p&gt;There are a few different approaches that can help minimize any friction users might encounter.&lt;/p&gt;

&lt;p&gt;At my company, Rivet, we supported IE 11 for the first year or so of the company’s life, and later decided to limit browser support to the latest two versions of the evergreen browsers (such as Chrome, Safari, Firefox, and Edge).&lt;/p&gt;

&lt;p&gt;Rather than pull the plug completely, we carefully crafted a fallback experience for users who try to access our application with a legacy browser. To do this, we ship a separate bundle — a mini-app — that supports all browsers and shows an instructional message to the user prompting them to download a modern browser if they wish to access our application.&lt;/p&gt;

&lt;p&gt;This added a little bit of complexity to our build and deploy processes, but has proven worth it; to simply show a blank page with a console full of errors would leave the user stranded and without a clear path on how to get unstuck.&lt;/p&gt;

&lt;p&gt;Our CTO even visited the office of one of our largest customers. Upon arrival, he witnessed one of our users open up our app in IE 11, see the message, and then promptly switch over to Chrome to continue using the app. It was certainly a relatively smooth experience compared to the alternative, which perhaps would have been an awkward moment of confusion followed by “Yeah… you’ll want to try that again in Chrome or Firefox.”&lt;/p&gt;

&lt;p&gt;If an automatic in-app message is not an option, there are other ways to ensure your users are as taken care of as possible:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An email marketing campaign informing and preparing users of the change&lt;/li&gt;
  &lt;li&gt;Custom error pages that direct to a help center or provide a way to get in contact with you when something goes wrong&lt;/li&gt;
  &lt;li&gt;A message that is pushed to users proactively using a chat widget like &lt;a href=&quot;https://www.intercom.com/&quot;&gt;Intercom&lt;/a&gt; or similar&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;alternative-option-progressive-enhancement&quot;&gt;Alternative option: progressive enhancement&lt;/h3&gt;

&lt;p&gt;Legacy browser support also doesn’t have to be an all-or-nothing endeavor. The spirit of backward compatibility and &lt;a href=&quot;https://resilientwebdesign.com/&quot;&gt;progressive enhancement&lt;/a&gt; runs deep into the web’s DNA.&lt;/p&gt;

&lt;p&gt;Responsive web design is the practice of offering a different design for your site or app depending on the size or type of device your user is using to interact with it. This idea is extended beyond aesthetic considerations to include behaviors and functionality.&lt;/p&gt;

&lt;p&gt;The idea of offering a different experience based on the capabilities of the user’s browser is as old as the web itself. You might, therefore, consider offering basic functionality to IE 11 users along with a prompt to upgrade to a modern, standards-based browser for more advanced features.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Ultimately, software is for humans, by humans, and is a way that we communicate with each other and make each other’s lives better.&lt;/p&gt;

&lt;p&gt;There is a delicate balance between pursuing a comfortable developer experience — therefore shortening the release cycle and allowing for more or higher quality software to be shipped more quickly — and enabling as many types of users as possible to gain value from an app.&lt;/p&gt;

&lt;p&gt;Dropping support for legacy browsers like IE 11 should be considered carefully and approached pragmatically and empathetically.&lt;/p&gt;
</content:encoded>
      <pubDate>Tue, 02 Mar 2021 16:02:44 +0000</pubDate>
      <guid>https://mjswensen.com/blog/should-you-drop-support-for-internet-explorer-11/</guid>
    </item>
    
    <item>
      <title>How to Make an .ico File Using the macOS Terminal</title>
      <link>https://mjswensen.com/blog/how-to-make-ico-file-macos-cli/</link>
      <description>Suppose you have an icon or logo at multiple resolutions and would like to combine them into a single `favicon.ico` file for your website. These are the commands you need to get up and running from the comfort of your own terminal.</description>
      <content:encoded>&lt;p&gt;Suppose you have an icon or logo at multiple resolutions and would like to combine them into a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;favicon.ico&lt;/code&gt; file for your website. These are the commands you need to get up and running from the comfort of your own terminal.&lt;/p&gt;

&lt;h2 id=&quot;step-1-install-imagemagick&quot;&gt;Step 1: install ImageMagick&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://imagemagick.org/script/download.php&quot;&gt;ImageMagick downloads page&lt;/a&gt; for instructions on how to get it running on your machine. Personally, I like to use containerization to keep my Mac free from extraneous software that I rarely use, so I’ll install ImageMagick in a Docker container instead (make sure you have Docker installed):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; /absolute/path/to/icons/directory:/workdir &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; /workdir ubuntu&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This command will drop you into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; session on the container. From there, it’s easy to get ImageMagick installed:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;apt update
apt upgrade
apt &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; imagemagick&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;div class=&quot;cards tip&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;span class=&quot;card-title&quot;&gt;Tip!&lt;/span&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;ImageMagick has a dependency on &lt;code&gt;tzdata&lt;/code&gt;, so you will be prompted to configure your time zone upon installation. Since this is an ephemeral installation, you can just type &lt;code&gt;1&lt;/code&gt; for each prompt to get through the installation process quickly.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h2 id=&quot;step-2-create-the-file&quot;&gt;Step 2: create the file&lt;/h2&gt;

&lt;p&gt;ImageMagick gives us a handy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;convert&lt;/code&gt; command that makes the process super easy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;convert input-file-16x16.png input-file-24x24.png input-file-32x32.png input-file-64x64.png favicon.ico&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This will create the desired &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;favicon.ico&lt;/code&gt; file.&lt;/p&gt;

&lt;h2 id=&quot;step-3-verify-the-ico-file&quot;&gt;Step 3: verify the .ico file&lt;/h2&gt;

&lt;p&gt;ImageMagick also provides a nice utility called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;identify&lt;/code&gt; for inspecting our file to ensure it is formatted as we expect.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;identify favicon.ico&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The output should look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;favicon.ico[0] ICO 16x16 16x16+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[1] ICO 24x24 24x24+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[2] ICO 32x32 32x32+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[3] ICO 64x64 64x64+0+0 8-bit sRGB 24838B 0.000u 0:00.000&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</content:encoded>
      <pubDate>Thu, 11 Feb 2021 13:03:36 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-to-make-ico-file-macos-cli/</guid>
    </item>
    
    <item>
      <title>You Might Not Need Sass: Modern CSS Techniques</title>
      <link>https://mjswensen.com/blog/you-might-not-need-sass-modern-css-techniques/</link>
      <description>Sass and other CSS preprocessors have some great features and have been widely used for years. CSS is rapidly evolving, though, and some of the features that were previously only possible through preprocessors are now achievable in plain CSS.</description>
      <content:encoded>&lt;p&gt;&lt;a href=&quot;https://sass-lang.com/&quot;&gt;Sass&lt;/a&gt; and other CSS preprocessors have some great features and have been widely used for years. CSS is rapidly evolving, though, and some of the features that were previously only possible through preprocessors are now achievable in plain CSS. While updating the styles for &lt;a href=&quot;https://mjswensen.com&quot;&gt;mjswensen.com&lt;/a&gt;, I found plain CSS to be powerful enough on its own and completely migrated away from Sass. Depending on the size and needs of your project (and target browser support), you may be able to simplify your CSS tool chain as well by using these techniques.&lt;/p&gt;

&lt;p&gt;The Sass features we will be re-creating in vanilla CSS in this article:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#parameterized-mixins&quot;&gt;Parameterized Mixins&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#color-manipulation&quot;&gt;Color Manipulation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#variables&quot;&gt;Variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;parameterized-mixins&quot;&gt;Parameterized Mixins&lt;/h2&gt;

&lt;p&gt;In Sass, you can define a collection of reusable styles, and then include the styles in multiple places throughout your stylesheet.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;k&quot;&gt;@mixin&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentColor&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1em&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.error&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.success&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Submitted successfully.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can achieve this same functionality in vanilla CSS using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/--*&quot;&gt;custom properties&lt;/a&gt; and some changes to our markup:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nc&quot;&gt;.alert&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;red&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert success&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Submitted successfully.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;tradeoffs&quot;&gt;Tradeoffs&lt;/h3&gt;

&lt;p&gt;Though we can achieve something similar to Sass’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@mixin&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@include&lt;/code&gt; with vanilla CSS, there are some tradeoffs to this approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is less clarity around how to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alert&lt;/code&gt; class since it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--color&lt;/code&gt; “parameter” is implicit; you have to reference the HTML to see how it is being used and what the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--color&lt;/code&gt; might be, whereas with Sass the parameters are explicit.&lt;/li&gt;
  &lt;li&gt;Sass can catch incorrect usages of mixins at compile time and surface errors to the developer, whereas the CSS approach will silently fail if used incorrectly.&lt;/li&gt;
  &lt;li&gt;Since the CSS approach involves both the stylesheet and the markup, there is more room for mistakes in refactoring a “mixin,” especially in large projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;color-manipulation&quot;&gt;Color Manipulation&lt;/h2&gt;

&lt;p&gt;Sass has long provided a powerful library for manipulating colors. Similar functionality has been proposed for CSS in the &lt;a href=&quot;https://drafts.csswg.org/css-color/#modifying-colors&quot;&gt;CSS Color Module Level 4 specification&lt;/a&gt; but is not available for widespread use at the time of writing. However, using the widely available CSS &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calc()&lt;/code&gt; function, we can achieve some simple color manipulations without any tooling on top of our CSS.&lt;/p&gt;

&lt;p&gt;In Sass, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rgba()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;desaturate()&lt;/code&gt; functions to get semi-transparent and desaturated versions of a given color, respectively:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$main-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;75%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;58%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$semi-transparent-main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$main-color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$desaturated-main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;desaturate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$main-color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can achieve the same effect in CSS by splitting the color components into their own custom properties and operating on them individually:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--main-color-h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--main-color-s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;75%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--main-color-l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;58%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--semi-transparent-main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;m&quot;&gt;0.1&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--desaturated-main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--main-color-l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;tradeoffs-1&quot;&gt;Tradeoffs&lt;/h3&gt;

&lt;p&gt;The Sass syntax for color manipulation is more compact, easier to use, and easier to read. There are also limitations to the CSS-only approach, at least until the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color()&lt;/code&gt; function becomes a W3C recommendation and gains browser adoption.&lt;/p&gt;

&lt;h2 id=&quot;variables&quot;&gt;Variables&lt;/h2&gt;

&lt;p&gt;One of the most then-groundbreaking features of CSS preprocessors was the ability to define and reuse values via variables. As seen in the examples above, we can now do the same thing in vanilla CSS using custom properties. But there are some nuances in the difference between Sass variables and custom properties that are worth mentioning.&lt;/p&gt;

&lt;p&gt;Since Sass is a compiled language (compiles down to CSS), variable values are evaluated at compile time rather than at runtime. This comes with a couple of tradeoffs to consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sass variables’ values can’t change in response to browser context changes (like media queries), while custom properties can.&lt;/li&gt;
  &lt;li&gt;Sass can warn you if you try to use a variable that hasn’t been initiated (if the name is misspelled, for example), while CSS will fall silently when using an undefined variable.&lt;/li&gt;
  &lt;li&gt;Both Sass variables and custom properties are “global,” but “global” has different meanings. For Sass, “global” means in scope for the current compilation target and any dependency stylesheets (and their dependencies). In CSS, “global” means really global: the values are visible to all stylesheets, markup, and scripts loaded on the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Modern vanilla CSS can cover some of the preprocessors’ flagship features, but at a cost of readability and maintainability. The overhead of a CSS build process may be worth it for larger projects or teams. For smaller or solo projects, though, CSS is powerful enough to stand on its own.&lt;/p&gt;
</content:encoded>
      <pubDate>Tue, 15 Sep 2020 12:46:12 +0000</pubDate>
      <guid>https://mjswensen.com/blog/you-might-not-need-sass-modern-css-techniques/</guid>
    </item>
    
    <item>
      <title>The Book of Mormon: An Ancient Guide to Modern Times</title>
      <link>https://mjswensen.com/blog/the-book-of-mormon-an-ancient-guide-to-modern-times/</link>
      <description>As millions of members of the Church of Jesus Christ around the world study the Book of Mormon together this year, I have been impressed by how similar the challenges, attitudes, and behaviors are between the ancient people in the record and people today.</description>
      <content:encoded>&lt;p&gt;As millions of members of the Church of Jesus Christ around the world study the Book of Mormon together this year, I have been impressed by how similar the challenges, attitudes, and behaviors are between the ancient people in the record and people today.&lt;/p&gt;

&lt;p&gt;This week we study an exchange between Alma, a reformed trouble maker who has become the leader over the ancient Church in that area, and Korihor, a self-proclaimed “Anti-Christ.” Alma and Korihor pre-date the life of Jesus Christ, so their discussion revolves around whether or not a Christ would come. Note Korihor’s key arguments as he contends with Alma:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p13#p13&quot;&gt;To believe in Christ is a burden, and a ridiculous and pointless one at that.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p13#p13&quot;&gt;No one can know the future.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p14#p14&quot;&gt;These beliefs are just tradition, nothing more.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p15#p15&quot;&gt;One can’t &lt;em&gt;know&lt;/em&gt; things that one can’t &lt;em&gt;see&lt;/em&gt;.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p27#p27&quot;&gt;Church leaders’ primary objective is to gain wealth and keep others from enjoying life.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p28#p28&quot;&gt;God has never been seen or known.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sound familiar? These similar to ideas that are very widespread in the modern world; thoughts shared by non-believers and even believers from time to time. Take a look at Alma’s rebuttals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p32-p34#p32&quot;&gt;Church leaders work to support themselves financially.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p39#p39&quot;&gt;I know that there is a God, and that Christ will come.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p40#p40&quot;&gt;There is no evidence that there is not a God.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p44#p44&quot;&gt;There is, however, much evidence of God’s existence: scriptures, prophets, Earth, animal and plant life, space, planets, science, etc.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Korihor tells Alma he will change is mind if Alma can provide proof of God’s power. To see how the story ends, read &lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm/alma/30.p43-p60#p43&quot;&gt;verses 43-60 of chapter 30 of the book of Alma in the Book of Mormon.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are lots of interesting things to learn from this account, but if anything, we learn that we’ve been here before—humans in ancient times are similar to us now, therefore &lt;strong&gt;we can learn from their experience to better ourselves and our world.&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;The Book of Mormon can be studied online for free at &lt;a href=&quot;https://www.churchofjesuschrist.org/study/scriptures/bofm&quot;&gt;ChurchOfJesusChrist.org&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
      <pubDate>Wed, 08 Jul 2020 12:24:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/the-book-of-mormon-an-ancient-guide-to-modern-times/</guid>
    </item>
    
    <item>
      <title>Building Stripe.com's Tabbed Preview Widget From Scratch in 30 Minutes</title>
      <link>https://mjswensen.com/blog/building-stripe-tabbed-preview-widget-from-scratch-in-30-min/</link>
      <description>In this video I try to build a fully functional tabbed preview widget in HTML and CSS from scratch in 30 minutes—without looking at the original code. After the time's up, I peek under the hood to see the approach of the original author and compare and contrast it to my approach.</description>
      <content:encoded>&lt;iframe src=&quot;//www.youtube.com/embed/NFOidUvke0k&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;In this video I try to build a fully functional tabbed preview widget in HTML and CSS from scratch in 30 minutes—without looking at the original code. After the time’s up, I peek under the hood to see the approach of the original author and compare and contrast it to my approach.&lt;/p&gt;

&lt;h1 id=&quot;the-build-process&quot;&gt;The build process&lt;/h1&gt;

&lt;h2 id=&quot;setting-up-a-development-environment&quot;&gt;Setting up a development environment&lt;/h2&gt;

&lt;p&gt;For this short project I used a very simple setup: an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file served by &lt;a href=&quot;https://npmjs.com/package/browser-sync&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;browser-sync&lt;/code&gt;&lt;/a&gt; for automatic reloads on save. It can be run without previous download or install via &lt;a href=&quot;https://www.npmjs.com/package/npx&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx&lt;/code&gt;&lt;/a&gt;, which is included by default in node/npm installations.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;npx browser-sync &lt;span class=&quot;nt&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--files&lt;/span&gt; index.html&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;configuring-the-widgets-container&quot;&gt;Configuring the widget’s container&lt;/h2&gt;

&lt;p&gt;Since this widget doesn’t appear to respond to the window size, I used the macOS screenshot tool (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;command&lt;/code&gt;-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shift&lt;/code&gt;-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;) to measure the dimensions and simply hard-code them into the CSS.&lt;/p&gt;

&lt;h2 id=&quot;managing-the-active-tab-state&quot;&gt;Managing the active tab state&lt;/h2&gt;

&lt;p&gt;The original Stripe implementation uses JavaScript to maintain the state of the currently active tab, which is a perfectly reasonable approach. I thought it might be fun to see if we could do it without JavaScript. I landed on using radio-type HTML inputs and some CSS selector tricks to achieve the same effect. In a professional setting, I would have likely used JavaScript—one could argue that this is an inappropriate use of radio inputs since this is an informational widget and not part of a form with user-provided data.&lt;/p&gt;

&lt;p&gt;The book I referenced in the video is called &lt;a href=&quot;https://resilientwebdesign.com/&quot;&gt;&lt;em&gt;Resilient Web Design&lt;/em&gt; by Jeremy Keith&lt;/a&gt; and is freely available to read online.&lt;/p&gt;

&lt;h2 id=&quot;sliding-the-content-left-and-right-based-on-the-active-tab&quot;&gt;Sliding the content left and right based on the active tab&lt;/h2&gt;

&lt;p&gt;I wrapped the code snippets in a container and positioned it absolutely, altering the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left&lt;/code&gt; property based on the active tab. We discuss a better approach to this later on when we inspect Stripe’s solution.&lt;/p&gt;

&lt;h2 id=&quot;giving-the-widget-a-3d-appearance&quot;&gt;Giving the widget a 3D appearance&lt;/h2&gt;

&lt;p&gt;To give the widget a 3D appearance, I rotated the widget around the X and Y axes, but it didn’t quite have the right effect. I should have used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rotate3d()&lt;/code&gt; function instead.&lt;/p&gt;

&lt;h2 id=&quot;adding-a-shine-effect&quot;&gt;Adding a shine effect&lt;/h2&gt;

&lt;p&gt;For the shine effect, I added an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo-element, positioned absolutely to stretch the width and height of the container, and added a background gradient. To keep the text beneath it selectable (and tabs clickable), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pointer-events: none&lt;/code&gt; was required so that mouse events would fall through it.&lt;/p&gt;

&lt;h2 id=&quot;my-final-code&quot;&gt;My final code&lt;/h2&gt;

&lt;p&gt;Here is what I ended up with at the end of the session.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;viewport&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;width=device-width, initial-scale=1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Code Preview Widget&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;490px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;--bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;#31335B&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;--radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;--border-active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;#596481&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;380px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1fr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1fr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;-10deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotateY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bottom&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;grid-column-start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;grid-column-end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;overflow-x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;400ms&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ease-in-out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#js&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#rb&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#py&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#go&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#other&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.slide-wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nc&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;radio&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;#js&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;#rb&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;rb&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;#py&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;py&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;#go&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;go&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;#other&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:checked&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--border-active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--border-active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--bg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;#2D2F4A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-top-left-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-top-right-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.25em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;--border-active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tab&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;js&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tab&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rb&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tab&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;py&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tab&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;go&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;radio&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;tab&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      Node.js
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;rb&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      Ruby
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;py&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      Python
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;go&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      Go
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;label&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;for=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      ...
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;slide-wrapper&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;// Set your secret key
  const stripe = require(&#39;stripe&#39;)(&#39;sk_test_BQokikJOvBiI2HlWgH4olfQ2&#39;);
  
  // Get the payment token ID submitted by the form:
  const token = request.body.stripeToken;
  
  (async () =&amp;gt; {
    const charge = await stripe.charges.create({
      amount: 999,
      currency: &#39;usd&#39;,
      description: &#39;Example charge&#39;,
      source: token,
    });
  })();&lt;span class=&quot;nt&quot;&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;# Set your secret key
  Stripe.api_key = &#39;sk_test_BQokikJOvBiI2HlWgH4olfQ2&#39;
  
  # Get the payment token ID submitted by the form:
  token = params[:stripeToken]
  
  charge = Stripe::Charge.create({
    amount: 999,
    currency: &#39;usd&#39;,
    description: &#39;Example charge&#39;,
    source: token,
  })&lt;span class=&quot;nt&quot;&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt; Set your secret key
  stripe.api_key = &#39;sk_test_BQokikJOvBiI2HlWgH4olfQ2&#39;
  
  # Get the payment token ID submitted by the form:
  token = request.form[&#39;stripeToken&#39;]
  
  charge = stripe.Charge.create(
    amount=999,
    currency=&#39;usd&#39;,
    description=&#39;Example charge&#39;,
    source=token,
  )&lt;span class=&quot;nt&quot;&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;// Set your secret key
  stripe.Key = &#39;sk_test_BQokikJOvBiI2HlWgH4olfQ2&#39;
  
  // Get the payment token ID submitted by the form:
  token := r.FormValue(&#39;stripeToken&#39;)
  
  params := &lt;span class=&quot;err&quot;&gt;&amp;amp;&lt;/span&gt;stripe.ChargeParams{
    Amount: 999,
    Currency: &#39;usd&#39;,
    Description: &#39;Example charge&#39;,
  }
  params.SetSource(token)
  ch, _ := charge.New(params)&lt;span class=&quot;nt&quot;&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;TODO&lt;span class=&quot;nt&quot;&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;inspecting-the-original-stripe-code&quot;&gt;Inspecting the original Stripe code&lt;/h1&gt;

&lt;h2 id=&quot;differences-with-my-approach&quot;&gt;Differences with my approach&lt;/h2&gt;

&lt;p&gt;Aside from the fact that the Stripe code was much more polished (with additional borders, typography, syntax highlighting, etc.), there were a number things about the original code that were much improved to my version. Here are a couple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rather than transitioning the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left&lt;/code&gt; property to slide the code back and forth, the original author used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;translateX()&lt;/code&gt;, which is more performant.&lt;/li&gt;
  &lt;li&gt;The original author used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; element to wrap this widget, which is much more semantically correct than the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; I used.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;nice-touches&quot;&gt;Nice touches&lt;/h2&gt;

&lt;p&gt;Stripe is known for adding a level of polish and detail that most engineering teams can only dream of. Here are just a couple that I noticed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo-elements with transparent-to-opaque background gradients, the text had an appearance of sliding “under” the edges of the widget as it moved back and forth.&lt;/li&gt;
  &lt;li&gt;A tasteful &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;box-shadow&lt;/code&gt; was added to augment the 3D effect of the widget.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;Any questions or suggestions about my tooling, approach, development style? What would you have done differently? I’d love to hear from you. &lt;a href=&quot;https://twitter.com/mjswensen&quot;&gt;Find me on Twitter&lt;/a&gt; or &lt;a href=&quot;https://youtu.be/NFOidUvke0k&quot;&gt;leave a comment on the video&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;tools&quot;&gt;Tools&lt;/h1&gt;

&lt;p&gt;Here are the tools I used in this video:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Text editor: &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; with theme &lt;a href=&quot;https://themer.dev/?colors.dark.shade0=%23171D1D&amp;amp;colors.dark.shade7=%23CDDEDE&amp;amp;colors.dark.accent0=%23F18CB1&amp;amp;colors.dark.accent1=%23B86675&amp;amp;colors.dark.accent2=%23C57B67&amp;amp;colors.dark.accent3=%2300ACBD&amp;amp;colors.dark.accent4=%23208490&amp;amp;colors.dark.accent5=%231A9BA6&amp;amp;colors.dark.accent6=%2332A0AC&amp;amp;colors.dark.accent7=%23FCA188&amp;amp;colors.light.shade0=%23F8FDFE&amp;amp;colors.light.shade7=%2305262D&amp;amp;colors.light.accent0=%23D75971&amp;amp;colors.light.accent1=%23CD2455&amp;amp;colors.light.accent2=%23AA582D&amp;amp;colors.light.accent3=%231D7E66&amp;amp;colors.light.accent4=%2314808C&amp;amp;colors.light.accent5=%230E7481&amp;amp;colors.light.accent6=%234797A7&amp;amp;colors.light.accent7=%23C87D4F&amp;amp;activeColorSet=light&amp;amp;calculateIntermediaryShades.dark=true&amp;amp;calculateIntermediaryShades.light=true&quot;&gt;&lt;em&gt;Right in the Teals&lt;/em&gt;&lt;/a&gt; by &lt;a href=&quot;https://github.com/mjswensen/themer&quot;&gt;themer&lt;/a&gt;, and &lt;a href=&quot;https://github.com/tonsky/FiraCode&quot;&gt;Fira Code&lt;/a&gt; font&lt;/li&gt;
  &lt;li&gt;Browser: &lt;a href=&quot;https://brave.com/mjs324&quot;&gt;Brave&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Development server: &lt;a href=&quot;https://browsersync.io/&quot;&gt;Browsersync&lt;/a&gt; launched under &lt;a href=&quot;https://www.npmjs.com/package/npx&quot;&gt;npx&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Application launcher: &lt;a href=&quot;https://www.alfredapp.com/&quot;&gt;Alfred&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Color picker: &lt;a href=&quot;https://sipapp.io/&quot;&gt;Sip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
      <pubDate>Thu, 09 Apr 2020 12:39:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/building-stripe-tabbed-preview-widget-from-scratch-in-30-min/</guid>
    </item>
    
    <item>
      <title>Playing the Numbers: A Millennial Engineer's Probabilistic Approach to Believing in God</title>
      <link>https://mjswensen.com/blog/playing-the-numbers/</link>
      <description>Though faith is an essential component to any religious conversation, I'd like to set it aside for just a moment and focus instead on a pragmatic and logical way of thinking about religion.</description>
      <content:encoded>&lt;div class=&quot;cards updates&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;span class=&quot;card-title&quot;&gt;Update 12/18/2019&lt;/span&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;Thanks to an astute Reddit commenter, today I learned about Pascal&#39;s Wager, which is strikingly similar to the idea I tried to synthesize below. For a more in-depth read on this topic, check out &lt;a href=&quot;https://en.wikipedia.org/wiki/Pascal&#39;s_wager&quot;&gt;the Wikipedia article on Pascal&#39;s Wager&lt;/a&gt;.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;A core premise (perhaps &lt;em&gt;the&lt;/em&gt; core premise) of religiosity is faith, which I define as “believing in things one cannot see or otherwise definitively prove.” Though faith is an essential component to any religious conversation, I’d like to set it aside for just a moment and focus instead on a pragmatic and logical way of thinking about religion.&lt;/p&gt;

&lt;p&gt;The topic in question: “Does God exist?”—a question that has sparked divisive debate for millennia. Believers will present mountains of “evidence” while non-believers will correctly point out that such “evidence” cannot amount to “proof.”&lt;/p&gt;

&lt;p&gt;Rather than pick my side and try to convince others of its relative merit, I take a different approach. Let’s look at the problem rationally, from a distance, by comparing the expected utility and implications of each option.&lt;/p&gt;

&lt;p&gt;For the purposes of illustration, let’s simplify the spectrum of possibilities down into two distinct categories: either there is a God, or there is not. The primary end goal of most religions is to influence people to live according to a moral code, usually set forth by deity. So again, let’s boil the infinitely broad spectrum of lifestyle choices into two simple categories: to obey God’s moral code or to not obey God’s moral code.&lt;/p&gt;

&lt;p&gt;For a visual aid, we plot our two dimensions onto an empty probability square.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God&lt;/th&gt;
        &lt;th&gt;No God&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Since the goal of most rational people is to maximize one’s own happiness, we’ll use a rough relative measure of happiness level for the values in our square.&lt;/p&gt;

&lt;p&gt;The doctrines of most religions include some flavor of an afterlife, so we’ll also add a third dimension to our square: this life vs. the afterlife.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God&lt;/th&gt;
        &lt;th&gt;No God&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;this life happiness / afterlife happiness&lt;/td&gt;
        &lt;td&gt;this life happiness / afterlife happiness&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;this life happiness / afterlife happiness&lt;/td&gt;
        &lt;td&gt;this life happiness / afterlife happiness&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;We’re ready to start filling out our diagram, starting with the left column. We might describe these cells as “the happiness level in both this life and the afterlife of people who obey God and who do not obey God, given God exists.”&lt;/p&gt;

&lt;p&gt;“Happiness in this life” is difficult to measure. Though &lt;a href=&quot;https://www.nber.org/papers/w11377&quot;&gt;some research suggests that religiosity is directly correlated with increased happiness, health, and wealth,&lt;/a&gt; in my observation faithful people do not have a corner market on happiness or prosperity, and they certainly aren’t immune to the challenges of being human. Conversely, despite what many non-religious people believe, living free from the rigors of a religious lifestyle isn’t necessarily a path to greater happiness in the long run. To keep our estimates conservative, we’ll assume that the happiness level of both religious and non-religious people is approximately the same. We’ll mark this level as “average.”&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God&lt;/th&gt;
        &lt;th&gt;No God&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;Average / ?&lt;/td&gt;
        &lt;td&gt;? / ?&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;Average / ?&lt;/td&gt;
        &lt;td&gt;? / ?&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;The afterlife dimension of these cells is more straightforward. Most religions concur that obedience to God in this life warrants some sort of reward in the afterlife; and disobedience results in some form of diminished happiness, at least temporarily.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God&lt;/th&gt;
        &lt;th&gt;No God&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;Average / High&lt;/td&gt;
        &lt;td&gt;? / ?&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;Average / Low&lt;/td&gt;
        &lt;td&gt;? / ?&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;On to the right column. We will reuse our previously calculated “Average” happiness level for both obedient and non-obedient people for this life into this column. Since there is no God and presumably therefore no afterlife in these scenarios, we’ll put “N/A” for the afterlife values.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God&lt;/th&gt;
        &lt;th&gt;No God&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;Average / High&lt;/td&gt;
        &lt;td&gt;Average / N/A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;Average / Low&lt;/td&gt;
        &lt;td&gt;Average / N/A&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;With our square filled in, we can now finish our analysis. Since the existence of God can neither be proven nor disproven (much to the chagrin of both believers and non-believers), we’ll naively assign an equal probability of 50% to both options. And in lieu of a more precise measure for happiness, we’ll make a simple assignment of 1, 2, and 3 to our rough levels.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;/th&gt;
        &lt;th&gt;God (0.5)&lt;/th&gt;
        &lt;th&gt;No God (0.5)&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Obey&lt;/td&gt;
        &lt;td&gt;Average (2) / High (3)&lt;/td&gt;
        &lt;td&gt;Average (2) / N/A&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Not Obey&lt;/td&gt;
        &lt;td&gt;Average (2) / Low (1)&lt;/td&gt;
        &lt;td&gt;Average (2) / N/A&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Now we can calculate the expected value for happiness for the two lifestyle choices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Obey: 0.5 × (2 + 3) + 0.5 × 2 = &lt;strong&gt;3.5&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Not obey: 0.5 × (2 + 1) + 0.5 × 2 = &lt;strong&gt;2.5&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our simple analysis suggests that obedient people will have a larger expected happiness, and therefore the rational choice to maximize well-being is to live in accordance to God’s moral code.&lt;/p&gt;

&lt;p&gt;Of course, life is infinitely more complex and our illustration may only serve as little more than a thought experiment, but a valid point still remains: banking life decisions on whether or not to live by a moral code solely on the existence or absence of God is an &lt;em&gt;enormous&lt;/em&gt; bet and strictly a &lt;em&gt;one-way ratchet&lt;/em&gt; in the sense that the upside is negligible with the potential for substantial downside.&lt;/p&gt;

&lt;p&gt;As for me, I like my choices to be the opposite: negligible downside with the potential for infinite reward.&lt;/p&gt;
</content:encoded>
      <pubDate>Wed, 18 Dec 2019 12:48:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/playing-the-numbers/</guid>
    </item>
    
    <item>
      <title>From Electron to Progressive Web App</title>
      <link>https://mjswensen.com/blog/from-electron-to-progressive-web-app/</link>
      <description>In this post I'd like to review my reasoning behind sunsetting an Electron-based application in favor of a Progressive Web App (PWA).</description>
      <content:encoded>&lt;p&gt;In this post I’d like to review my reasoning behind sunsetting an &lt;a href=&quot;https://electronjs.org&quot;&gt;Electron&lt;/a&gt;-based application in favor of a &lt;a href=&quot;https://developers.google.com/web/progressive-web-apps/&quot;&gt;Progressive Web App (PWA)&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;cards tldr&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;span class=&quot;card-title&quot;&gt;tl;dr&lt;/span&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;ul&gt;
        &lt;li&gt;Choose your platform wisely—you are at the mercy of its maintainers&lt;/li&gt;
        &lt;li&gt;URLs are amazing&lt;/li&gt;
        &lt;li&gt;You really can&#39;t beat the distribution model of the Web&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;h1 id=&quot;themer&quot;&gt;themer&lt;/h1&gt;

&lt;p&gt;First a little context. My application, &lt;a href=&quot;https://github.com/mjswensen/themer&quot;&gt;themer&lt;/a&gt;, started off as a command-line tool built on Node.js. It uses a set of colors as input and produces matching themes for various text editors, terminal emulators, and other tools. However, actually &lt;em&gt;choosing&lt;/em&gt; the colors for your personalized theme was tedious and cumbersome. A friend suggested to me that I build a GUI to help ease this process and provide a tighter feedback loop when creating a personalized theme. Given that Electron bundles a Node.js runtime, and that I could therefore easily wrap my CLI in a “native” GUI for all platforms, an Electron app seemed like a perfect fit. themer’s GUI was born:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/themer-gui.png&quot; alt=&quot;themer&#39;s electron GUI&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;the-bug&quot;&gt;The bug&lt;/h1&gt;

&lt;p&gt;After spending a good amount of time building the GUI and wiring it up to my CLI running in Electron’s Node process, I was able to bundle up the application installers and make them available for download on the &lt;a href=&quot;https://github.com/mjswensen/themer-gui/releases&quot;&gt;GitHub releases&lt;/a&gt; page. It wasn’t until after the application was downloaded about 10k times that I discovered &lt;a href=&quot;https://github.com/electron/electron/issues/13596&quot;&gt;a really nasty bug&lt;/a&gt; that caused Electron-based applications to crash when using the macOS native color picker:&lt;/p&gt;

&lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot;&gt;
  &lt;source src=&quot;/blog/videos/themer-gui-crash.webm&quot; type=&quot;video/webm&quot; /&gt;
  &lt;source src=&quot;/blog/videos/themer-gui-crash.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;p&gt;It took nearly eight months to fix that issue, during which the utility of themer’s GUI was drastically reduced.&lt;/p&gt;

&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;

&lt;p&gt;When building for a particular platform or framework, &lt;strong&gt;you are at the mercy of its maintainers&lt;/strong&gt; (of course, if the project is open-source, you can attempt to fix problems yourself, but this isn’t always tenable). Even though Electron is popular, a bug like this wouldn’t have lasted a week in a mainstream Web browser (or likely occurred at all).&lt;/p&gt;

&lt;h1 id=&quot;sharedpersisted-state&quot;&gt;Shared/persisted state&lt;/h1&gt;

&lt;p&gt;One requirement for themer’s GUI was that users should be able to save (and ideally share) their themes. Short of some sort of cloud-based service that integrated with the GUI, the best solution was to simply allow themer’s GUI to read and write JSON from the filesystem. One neat thing about Electron is that you can register a custom icon with the operating system for files that match your custom extension:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/themer-file.png&quot; alt=&quot;themer GUI&#39;s custom .thmr file extension and icon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I went the whole nine yards and implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Save&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Save As...&lt;/code&gt;, friendly prompts to prevent accidental discarding of unsaved changes, etc…. basically everything you’d expect from a traditional desktop program. This worked well enough for persisting theme data, but didn’t allow for easily sharing themes with others.&lt;/p&gt;

&lt;p&gt;On the Web, there’s a better way to share application state: the URL. themer’s new PWA stores all of the theme-relevant state in the URL’s query string, so sharing your theme is as easy as sending a link (like &lt;a href=&quot;https://themer.dev/?colors.dark.accent0=%23CA3E5A&amp;amp;colors.dark.accent1=%23D8843E&amp;amp;colors.dark.accent2=%23EBB062&amp;amp;colors.dark.accent3=%2381A559&amp;amp;colors.dark.accent4=%2342ABAB&amp;amp;colors.dark.accent5=%234496CD&amp;amp;colors.dark.accent6=%239770B2&amp;amp;colors.dark.accent7=%23B35D8D&amp;amp;colors.dark.shade0=%2313222E&amp;amp;colors.dark.shade7=%23ACBECC&amp;amp;activeColorSet=dark&amp;amp;calculateIntermediaryShades.dark=true&amp;amp;calculateIntermediaryShades.light=true&quot;&gt;this one&lt;/a&gt;), and saving is as easy as bookmarking (or just using your browser’s history).&lt;/p&gt;

&lt;h2 id=&quot;the-takeaway-1&quot;&gt;The takeaway&lt;/h2&gt;

&lt;p&gt;The URL is one of &lt;strong&gt;the Web’s most wonderful and distinguishing features.&lt;/strong&gt; This simple and powerful state/location sharing mechanism is one of my favorite parts about the Web, and is glaringly missing from other platforms.&lt;/p&gt;

&lt;h1 id=&quot;distribution-model&quot;&gt;Distribution model&lt;/h1&gt;

&lt;p&gt;There is a great open-source package available called &lt;a href=&quot;https://www.electron.build/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;electron-builder&lt;/code&gt;&lt;/a&gt; that adds support for pushing updates that your app can automatically download in the background. In all, it was relatively easy to configure and get working, but there was always a voice in the back of my mind that said: “I hope the updater code in this version works; if it doesn’t, no one will be able to install updates beyond this point.”&lt;/p&gt;

&lt;p&gt;While it’s still possible to shoot yourself in the foot with a PWA’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceWorker&lt;/code&gt;, more of the cacheing and network code lives in the browser itself, leaving less room for error in user-land.&lt;/p&gt;

&lt;h2 id=&quot;the-takeaway-2&quot;&gt;The takeaway&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The distribution model for the Web is unparalleled.&lt;/strong&gt; If you ship a version of your application that is broken, all you have to do is fix it and re-deploy. The platform itself takes care of the rest.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;There are, of course, applications that require the use of Electron (like VS Code for filesystem access, etc.), in which case the choice to build a purely Web-based application is not so simple as it was for my small pet project.&lt;/p&gt;

&lt;p&gt;Electron is a wonderful piece of software, and building an Electron app was an enjoyable experience filled with learning. After having spent some time with it, though, I am more committed than ever to building for my platform of choice: the Web. I can’t wait to see what the future holds for PWAs and Web technologies in general.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href=&quot;https://themer.dev&quot;&gt;themer’s new PWA&lt;/a&gt; and let me know what you think.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 24 Mar 2019 02:29:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/from-electron-to-progressive-web-app/</guid>
    </item>
    
    <item>
      <title>Zen and the Art of Software Engineering</title>
      <link>https://mjswensen.com/blog/zen-and-the-art-of-software-engineering/</link>
      <description>As part of my on-boarding as software engineer at Nuvi, I had the opportunity to read the classic book "Zen and the Art of Motorcycle Maintenance" by Robert M. Pirsig. While the book delves deeply into abstract philosophical concepts, there were a number of concrete principles that I felt had direct application to writing high-quality software.</description>
      <content:encoded>&lt;p&gt;As part of my on-boarding as software engineer at &lt;a href=&quot;https://www.nuvi.com/&quot;&gt;Nuvi&lt;/a&gt;, I had the opportunity to read the classic book &lt;em&gt;Zen and the Art of Motorcycle Maintenance: An Inquiry into Values&lt;/em&gt; by Robert M. Pirsig. While the book delves deeply into abstract philosophical concepts, there were a number of concrete principles that I felt had direct application to writing high-quality software.&lt;/p&gt;

&lt;h2 id=&quot;the-efficacy-of-a-machine&quot;&gt;The efficacy of a machine&lt;/h2&gt;

&lt;p&gt;Pirsig points out that a machine (or system or tool) really has no inherent efficacy; but rather its efficacy is measured by how satisfied we are with it. I thought this was a compelling point of view because it provides us with a useful measuring stick for our software. For example, I could build a beautiful UI that I believe to be streamlined and intuitive, but if our users have different needs, the tool is not effective for them.&lt;/p&gt;

&lt;p&gt;This also applies to developers as they build the software. The book discusses how the true artisan is actively involved in their work, being one with the material (the tools, technologies, and code), and has peace of mind when producing good work. If there isn’t peace of mind, it could be a sign of subpar work or an ineffective environment.&lt;/p&gt;

&lt;h2 id=&quot;being-stuck-isnt-a-bad-thing&quot;&gt;Being stuck isn’t a bad thing&lt;/h2&gt;

&lt;p&gt;An eye-opening point brought up in the book is the idea that being mentally stuck is not a bad thing. It’s so common in day-to-day software work to run into roadblocks, and they are justifiably seen as annoyances that keep engineers from their goal to deliver software. However, these roadblocks provide opportunities for creativity—to think “laterally” as Pirsig calls it—to find new solutions and to innovate.&lt;/p&gt;

&lt;h2 id=&quot;gumption-is-psychic-gasoline&quot;&gt;Gumption is psychic gasoline&lt;/h2&gt;

&lt;p&gt;Though nearly 40 years later the word “gumption”—defined as shrewd or spirited initiative and resourcefulness—seems somewhat outdated, it accurately describes the creative fuel we need to produce quality work. We should work to ensure that our proverbial tanks of gumption don’t deplete. Pirsig dedicates a significant amount of time in the book to discuss the sources and drains of gumption. These are the ones that stuck out to me:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Don’t try to regain gumption by working furiously to make up lost time. It doesn’t work for motorcycles or for software.&lt;/li&gt;
  &lt;li&gt;Sometimes making your own motorcycle parts will build gumption. In the software world, I think of this as finding and building the right abstractions in code.&lt;/li&gt;
  &lt;li&gt;Ego is a gumption trap. So is anxiety about failure.&lt;/li&gt;
  &lt;li&gt;Bad tools are also a gumption trap.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Creating quality work is an all-encompassing act, requiring conscious effort that is more often focused inwardly rather than on the work itself. To adapt Pirsig’s own words: “The real [software] you’re working on is the [software] of yourself.”&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 25 Mar 2017 00:58:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/zen-and-the-art-of-software-engineering/</guid>
    </item>
    
    <item>
      <title>GitHub Pages vs. GitLab Pages</title>
      <link>https://mjswensen.com/blog/github-pages-vs-gitlab-pages/</link>
      <description>I've been a longtime fan of GitHub Pages. It is a wonderful option for hosting many types of sites. However, recent movements toward a more secure web and GitHub's lack of support for HTTPS on GitHub Pages with a custom domain prompted me to start looking at other options, including GitLab Pages.</description>
      <content:encoded>&lt;p&gt;I’ve been a longtime fan of &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;. It is a wonderful option for hosting many types of sites. However, recent movements toward a more secure web and &lt;a href=&quot;https://github.com/isaacs/github/issues/156&quot;&gt;GitHub’s lack of support for HTTPS on GitHub Pages with a custom domain&lt;/a&gt; prompted me to start looking at other options, including &lt;a href=&quot;https://pages.gitlab.io/&quot;&gt;GitLab Pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;GitLab Pages has a lot of great features, many of which are similar to those of GitHub Pages (there’s no question that they are shooting for feature parity with the large incumbent). But there was one problem that was an absolute show-stopper for me: painfully slow build queues. For that reason primarily, I moved back over to GitHub Pages.&lt;/p&gt;

&lt;p&gt;These are the primary differences I found between the two services:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;GitHub Pages&lt;/th&gt;
      &lt;th&gt;GitLab Pages&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Static site generator support&lt;/td&gt;
      &lt;td&gt;Jekyll only, no plugins&lt;/td&gt;
      &lt;td&gt;Completely customizable build, therefore any SSG is supported (including plugins)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Build configuration&lt;/td&gt;
      &lt;td&gt;Optional; Jekyll sites will be built automatically, and non-Jekyll sites are published directly. Tests can be set up easily through &lt;a href=&quot;https://travis-ci.org/&quot;&gt;travis-ci.org&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Required; without a valid build definition GitLab Pages won’t know how to deploy your site. Setting up the build definition in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitlab-ci.yml&lt;/code&gt; is easy and is very similar to setting up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.travis.yml&lt;/code&gt; for GitHub Pages project&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Build queue time&lt;/td&gt;
      &lt;td&gt;Almost always instantaneous, both on the GitHub Pages side and the Travis CI side&lt;/td&gt;
      &lt;td&gt;Excruciatingly slow; of the several builds I triggered on my site, the average wait time was a couple of hours&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Support for HTTPS&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/isaacs/github/issues/156&quot;&gt;Partial&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://about.gitlab.com/2016/04/07/gitlab-pages-setup/#custom-domains&quot;&gt;Yes&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CDN support&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://github.com/blog/1715-faster-more-awesome-github-pages&quot;&gt;Out-of-the-box&lt;/a&gt;&lt;/td&gt;
      &lt;td&gt;Not provided by GitLab Pages but can be configured with free tiers of popular CDN providers&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;GitLab Pages has a lot of great features. And the GitLab.com service is generally fantastic, especially given that it’s free. One of my favorite parts is that they offer free build instances (they call them “runners”) as part of the GitLab.com offering (and the CI is built right into the core product—another plus). The only issue is that since the runners are free, they are overworked and the build queues can be hours long (at the time of writing, at least). While it’s still a wonderful service, the delayed builds means a feedback loop that is too slow for feature development and a production deploy time that is too unpredictable.&lt;/p&gt;

&lt;p&gt;That slowness and the need to configure CDN myself outweighed my desire to enable TLS on my site for now. And given GitHub’s track record, I also have a sneaking suspicion that TLS support on GitHub Pages is just around the corner. In which case I’ll be extra glad I stayed.&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 01 Jul 2016 03:59:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/github-pages-vs-gitlab-pages/</guid>
    </item>
    
    <item>
      <title>Keeping Our Testimonies Up-to-date</title>
      <link>https://mjswensen.com/blog/keeping-our-testimonies-up-to-date/</link>
      <description>We are constantly changing, growing, and learning as we progress through life. Because of this, our testimonies need to be growing at a proportional pace.</description>
      <content:encoded>&lt;p&gt;I had the opportunity to give a talk on this topic to a congregation of &lt;a href=&quot;https://www.churchofjesuschrist.org/&quot;&gt;the Church of Jesus Christ&lt;/a&gt; in South Jordan, UT. The following is the transcript. The original topic title was “Finding Peace Through Jesus Christ by Strengthening Your Testimony Throughout Your Entire Life”.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Good morning brothers and sisters, I’m Brother Swensen. Today I will be talking about testimony. But I won’t be talking about it in the way we normally do. Often we think of a testimony as a belief or assurance of a particular point of doctrine or principle. And usually we think of these testimonies as knowledge gained by some specific experience in our lives, that can then be added to our collection of experience to be recalled later when we need it. Today, however, I am trying to shift my own thinking of what a testimony is—and I invite you to do the same—by thinking about testimony in the context of the passage of time; so rather than thinking of our testimonies as pieces of knowledge gained at a particular point in time, instead thinking of them as a living foundation that changes and adapts over time.&lt;/p&gt;

&lt;h2 id=&quot;testimonies-need-updating&quot;&gt;Testimonies Need Updating&lt;/h2&gt;

&lt;p&gt;As my sweet wife pointed out to me while I was studying to prepare this talk, an absolutely essential facet to our Heavenly Father’s Plan of Salvation is that we learn and grow constantly over time, “line upon line, precept upon precept” (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/bofm/2-ne/28.30#29&quot;&gt;2 Nephi 28:30&lt;/a&gt;). As part of that growing process, we acquire additional responsibility throughout our lives, in both spiritual and temporal matters. Typically, also the challenges and trials that we face increase in difficulty as we age and mature. Because of this, our testimonies need to be growing at a proportional pace. The testimony that we had at age 5, or 16, or 27 will not be sufficient alone to sustain us at age 43 or 72 or 96.&lt;/p&gt;

&lt;p&gt;I am a software engineer by trade, and as such, I spend most of my day on the computer. What many people don’t realize, is that a software program is never really “finished.” (Which is good, because it means job security for me!) There are always features to be added or fine tuned, enhancements than can be made, or problems that need to be fixed. Indeed, I spend most of my professional time publishing updated versions of my company’s software for the people who use it. Naturally, I am the designated technology fixer-upper for our family. And with this background, obviously when one of our computers or phones are not behaving properly, the first thing that I check is whether or not there are updates available for the software on the problematic device. More often than not, the problems that Christine and I run into with our technology are fixed when I make sure that everything is up-to-date.&lt;/p&gt;

&lt;p&gt;Our testimonies are the same way. We need to make sure that our testimonies are constantly updated for our spirits to be in tip-top shape. When we are sharing spiritual experiences, if we are needing to always refer back to long-past missionary service or an experience we had many years ago, it’s time for a testimony makeover! While those experiences are still special and important, we should be able to say “last week…” or “yesterday…” or “this morning I had a testimony-building experience.” Alma taught us this in the Book of Mormon when he asked this probing question: “And now behold, I say unto you, my brethren, if ye have experienced a change of heart, and if ye have felt to sing the song of redeeming love, I would ask, can ye feel so now?” (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/bofm/alma/5.26#25&quot;&gt;Alma 5:26&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;testimonies-need-to-be-as-dynamic-as-our-modern-day-lives&quot;&gt;Testimonies Need To Be As Dynamic As Our Modern-day Lives&lt;/h2&gt;

&lt;p&gt;Not only do our testimonies need to be current, they need to be broad and dynamic. The Greek philosopher Heraclitus once said that the only thing that is constant in life is change. Our circumstances are indeed constantly changing, as are our thoughts, needs, worries, strengths, weaknesses, doubts, struggles, and questions. Our testimonies need to adapt to our changing needs. For example, when I am teaching the missionary preparation class, I need to be able to bear sincere testimony of the material being taught, which is different every week. (I am still learning how to do that and have a long way to go.) The same can be said of missionaries, Sunday School instructors, parents, grandparents, visiting teachers, home teachers, and so forth.&lt;/p&gt;

&lt;p&gt;Our testimonies need to be living, dynamic, and ever evolving. This is the reason that we invariably hold testimony meetings every single month, instead of proclaiming our convictions just once when we gain our initial testimony.&lt;/p&gt;

&lt;p&gt;Brothers and sisters, gaining and maintaining a dynamic, current, multi-dimensional testimony is what will allow us to have peace in our lives. It is by believing in the Savior and living His Gospel that we find this type of testimony and accompanying peace. As the beloved hymn states: “Where can I turn for peace? Where is my solace when other sources cease to make me whole? .. He, only One. … In my Gethsemane, Savior and Friend. Gentle the peace he finds for my beseeching. Constant he is and kind. Love without end.” (&lt;a href=&quot;https://www.churchofjesuschrist.org/music/library/hymns/where-can-i-turn-for-peace&quot;&gt;“Where Can I Turn for Peace?”, Hymns, 129&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;finding-peace-and-testimony-by-believing-in-and-living-the-gospel-of-jesus-christ&quot;&gt;Finding Peace and Testimony by Believing in and Living the Gospel of Jesus Christ&lt;/h2&gt;

&lt;p&gt;In the &lt;a href=&quot;https://www.churchofjesuschrist.org/manual/teachings-of-presidents-of-the-church-howard-w-hunter/chapter-1-jesus-christ-our-only-way-to-hope-and-joy.p44&quot;&gt;very first chapter of Teachings of the Presidents of the Church: Howard W. Hunter&lt;/a&gt;, President Hunter teaches: “In this age, as in every age before us and in every age that will follow, the greatest need in all the world is an active and sincere faith in the basic teachings of Jesus of Nazareth, the living Son of the living God. Because many reject those teachings, that is all the more reason why sincere believers in the gospel of Jesus Christ should proclaim its truth and show by example the power and peace of a righteous, gentle life. …”&lt;/p&gt;

&lt;p&gt;Fortunately, God has outlined the pattern for our lives in the scriptures in various formats. Of course, the example of the Savior Himself is the foremost point of reference for how we must try to live our lives. He has laid the path for us, setting the example in every detail for how we can return to live with Him again in the Celestial Kingdom.&lt;/p&gt;

&lt;p&gt;When we are living a particular principle of the Gospel, Moroni teaches us that we can ask Heavenly Father to bless us with a testimony of that principle: “And when ye shall receive these things, I would exhort you that ye would ask God, the Eternal Father, in the name of Christ, if these things are not true; and if ye shall ask with a sincere heart, with real intent, having faith in Christ, he will manifest the truth of it unto you, by the power of the Holy Ghost. And by the power of the Holy Ghost ye may know the truth of all things. And whatsoever thing is good is just and true; wherefore, nothing that is good denieth the Christ, but acknowledgeth that he is.” (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/bofm/moro/10.4-6#3&quot;&gt;Moroni 10:4-6&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Even if we have doubts or questions, the core truths of the Gospel can be an anchor to us. In the talk &lt;a href=&quot;https://www.churchofjesuschrist.org/general-conference/2015/04/returning-to-faith#p23&quot;&gt;“Returning to Faith” by Sister Rosemary M. Wixom&lt;/a&gt; in the April sessions of General Conference last year, Sister Wixom quotes a young woman who was struggling to maintain her testimony when she received an inspired calling to play the piano for the primary. She said: “Playing the Primary songs, she often thought to herself, ‘Here are truths I love. I can still bear testimony. I will just say those things that I know and trust. It may not be a perfect offering of knowledge, but it will be my offering. What I focus on expands inside of me. It is beautiful to get back to the essence of the gospel and feel clarity.’” The young woman was actively working on updating her testimony by focusing first on the core doctrines that are taught in Primary.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Brothers and sisters, I know that the Savior and the Gospel can give us a peace that no worldly pursuit can ever offer us. I know this because I have put it to the test, usually by my own mistakes or misguided actions. There is no other way to enjoy the sweet peace of striving to live a righteous life. I know that the most powerful testimonies are the ones that aren’t stagnant or outdated, but rather that grow with us as we move forward through life. I encourage you to consider the changes you may need to make in your life to more fully enjoy the sweet peace and blessings that the Savior is so willing to give to us. I know that He loves us and that He is there, closer to us than we usually realize. I close with some of his tender words from the New Testament: “Peace I leave with you, my peace I give unto you: not as the world giveth, give I unto you. Let not your heart be troubled, neither let it be afraid.” (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/nt/john/14.27#26&quot;&gt;John 14:27&lt;/a&gt;). May we find peace through Him by strengthening our testimonies throughout our entire lives. In the name of Jesus Christ, amen.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 15 May 2016 15:25:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/keeping-our-testimonies-up-to-date/</guid>
    </item>
    
    <item>
      <title>How to Learn From Mistakes</title>
      <link>https://mjswensen.com/blog/how-to-learn-from-mistakes/</link>
      <description>Mistakes are an inevitable and essential part of the human experience. Though everybody makes mistakes of some form or another, not everyone leverages the great potential for learning and growth that mistakes offer. Here are some ideas for taking advantage of our mistakes rather than simply letting them weigh us down.</description>
      <content:encoded>&lt;p&gt;Mistakes are an inevitable and essential part of the human experience. Though everybody makes mistakes of some form or another, not everyone leverages the great potential for learning and growth that mistakes offer. Here are some ideas for taking advantage of our mistakes rather than simply letting them weigh us down.&lt;/p&gt;

&lt;h2 id=&quot;allow-yourself-to-feel-the-pain&quot;&gt;Allow yourself to feel the pain&lt;/h2&gt;

&lt;p&gt;Mistakes hurt. There’s no sugar-coating it: when we screw up, we cause pain for ourselves and those around us. Realizing that we’ve hurt a loved one can be a source of some of the deepest emotional pain we experience.&lt;/p&gt;

&lt;p&gt;We are pre-programmed with a natural aversion to pain, which helps us survive and avoid dangerous situations. We can use this aversion to our advantage when we make mistakes. Rather than avoid the pain we are experiencing after making a mistake—as many often do—we should instead embrace the pain and let it run its full course to help us fully learn our lesson.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Pain avoidance&lt;/th&gt;
        &lt;th&gt;Pain acceptance&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&quot;Medicating&quot; with alcohol or addictive substance/behavior&lt;/td&gt;
        &lt;td&gt;Meditating on the events leading up to the mistake&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Mentally denying that a mistake was made&lt;/td&gt;
        &lt;td&gt;Pondering the implications and consequences of the mistake&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;It may seem counterintuitive at first, but actively accepting the pain allows us to feel the depth of it and let our natural aversion to pain do its job when we later remember what we’ve experienced. If we avoid it altogether, we don’t allow ourselves that learning opportunity.&lt;/p&gt;

&lt;h2 id=&quot;set-goals-for-yourself&quot;&gt;Set goals for yourself&lt;/h2&gt;

&lt;p&gt;Setting goals is a proven practice for personal growth. Setting goals in relation to and in reaction to a mistake helps restore lost self-confidence and brings hope for the future. Set goals that will help you avoid making the same mistake again. Also set other worthwhile goals that may be unrelated but that will lead to a balanced life. Here are some examples:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Arise earlier each morning to allow for exercise or mediation&lt;/li&gt;
  &lt;li&gt;Spend a little time each day on behalf of others&lt;/li&gt;
  &lt;li&gt;Study from good books for 30 minutes each day&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;write-a-letter-to-your-children-or-your-future-self&quot;&gt;Write a letter to your children (or your future self)&lt;/h2&gt;

&lt;p&gt;Record your mistake and your experience with the pain it brought in a letter or journal format. It may sound strange, but there is something fulfilling about providing hard-earned wisdom to others in writing, even if your letter is never read. If it is actually read someday, you are altruistically giving the reader knowledge that they didn’t have to learn the hard way. If not, the act of writing down your experience is still therapeutic.&lt;/p&gt;

&lt;h2 id=&quot;do-your-best-to-rectify-the-problems-youve-caused&quot;&gt;Do your best to rectify the problems you’ve caused&lt;/h2&gt;

&lt;p&gt;Very few mistakes are completely unrecoverable. Use your creativity to come up with ways to minimize the damage. Also, if you hurt others, seek forgiveness from those people. Though your healing shouldn’t be contingent on receiving the forgiveness of others—you can’t control what they choose to do or say, after all—some of life’s most beautiful experiences involve sincere apologies we offer to those we care about.&lt;/p&gt;

&lt;h2 id=&quot;involve-god&quot;&gt;Involve God&lt;/h2&gt;

&lt;p&gt;We are children of a loving Heavenly Father who weeps when we are in pain and who rejoices when we learn from our mistakes. We can turn to Him with our concerns, and He can help us with strength to undertake any of the processes outlined in this article. If your mistake was moral in nature, ask Him for forgiveness and tell Him of your resolve to improve; He will answer that prayer and help you feel of His unconditional love for you.&lt;/p&gt;

&lt;p&gt;To learn more about forgiveness through Jesus Christ, visit &lt;a href=&quot;https://www.comeuntochrist.org/beliefs/jesus-christ&quot;&gt;comeuntochrist.org&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;forgive-yourself&quot;&gt;Forgive yourself&lt;/h2&gt;

&lt;p&gt;Understand that making mistakes is part of the human experience. Even still, forgiving oneself is not as easy as it sounds. Here are some ideas for starting on that challenging process:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Talk with a trusted friend or family member who is capable of encouraging you and building you up.&lt;/li&gt;
  &lt;li&gt;Make a list of accomplishments or positive points about yourself.&lt;/li&gt;
  &lt;li&gt;Think on a time when you were able to forgive someone else, and apply those feelings toward yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also cultivate a love of self by doing the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Show self-respect by living healthily (food, sleep, exercise, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;let-it-go&quot;&gt;Let it go!&lt;/h2&gt;

&lt;p&gt;Finally, after you have extracted all of the learning and growth potential out of a mistake, let it go and move on. Keep only enough of it in your memory to not make the same mistake twice. Carrying it any further is a waste of energy!&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;As outlined in one of my new favorite books, &lt;a href=&quot;/picks/the-pursuit-of-perfect-by-tal-ben-shahar/&quot;&gt;&lt;em&gt;The Pursuit of Perfect: How to Stop Chasing Perfection and Start Living a Richer, Happier Life&lt;/em&gt; by Tal Ben-Shahar&lt;/a&gt;, failure is a required and essential element of growth. But our mistakes will only be useful to us if we learn to let them propel us to greater heights!&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 26 Dec 2015 23:29:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-to-learn-from-mistakes/</guid>
    </item>
    
    <item>
      <title>Static Sites Are Awesome</title>
      <link>https://mjswensen.com/blog/static-sites-are-awesome/</link>
      <description>When creating landing pages, content sites, or blogs, content management systems shouldn't be the first tool web developers reach for. Because of the many benefits that come with the simplicity of static sites, they are not only a viable alternative, they are a more practical alternative. And with the popularity of static site generators rapidly on the rise, creating and maintaining static websites is easier than ever.</description>
      <content:encoded>&lt;p&gt;When creating landing pages, content sites, or blogs, content management systems shouldn’t be the first tool web developers reach for. Because of the many benefits that come with the simplicity of static sites, they are not only a viable alternative, they are a &lt;em&gt;more practical&lt;/em&gt; alternative. And with the popularity of static site generators rapidly on the rise, creating and maintaining static websites is easier than ever.&lt;/p&gt;

&lt;h2 id=&quot;why-static-sites&quot;&gt;Why static sites?&lt;/h2&gt;

&lt;p&gt;For certain types of content, static websites excel over content management systems in lots of areas, including performance, decreased server maintenance, site maintainability, and capacity for version-controlled content.&lt;/p&gt;

&lt;h3 id=&quot;performance&quot;&gt;Performance&lt;/h3&gt;

&lt;p&gt;In today’s world where site performance is of utmost importance, the most compelling reason to move toward static websites is the performance benefit. Content management systems make use of a database, and reading from the database to serve page content incurs a performance cost. Static sites make no use of a database—all the content is baked right into the final HTML code. The performance win comes from the fact that modern webservers (like Apache or nginx) are heavily optimized to serve such static files extremely quickly.&lt;/p&gt;

&lt;p&gt;In a nutshell, with a CMS, the burden of constructing a web page is placed on the server, at the time the page is requested. With a static site, that burden is moved to when the site is assembled before it is deployed. Many programmers will agree that, from a user experience perspective, it is better to have as much processing occur as possible at compile time rather than at run time. This is the approach that static site generators take.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/cms-vs-static-website-generator.png&quot; alt=&quot;Content management systems performance vs. static site generators performance&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;server-maintenance&quot;&gt;Server maintenance&lt;/h3&gt;

&lt;p&gt;Another big advantage for static sites is the reduced amount of required server maintenance. With a typical CMS like Wordpress or Drupal, sysadmins will need to install and configure a scripting language runtime (like PHP), a webserver (like Apache), and a database server (like MySQL). Security patches and periodic software updates will also need to be run for each of these. With static sites, only a webserver to serve the static files is required. (It is also arguable that security of the server is improved with this simpler setup.)&lt;/p&gt;

&lt;h3 id=&quot;maintainability&quot;&gt;Maintainability&lt;/h3&gt;

&lt;p&gt;Some scoff at the idea of building a static website because it sounds to the unfamiliar ear like writing static webpages individually by hand, which of course is a cumbersome task that content management systems were designed to overcome. However, the “static” part is simply the compile target. Static site generators allow web developers to build sites with familiar programming constructs, like DRY, logic and flow control, templating, etc.&lt;/p&gt;

&lt;h3 id=&quot;version-control&quot;&gt;Version control&lt;/h3&gt;

&lt;p&gt;Without the dependency on a database system, it is also much easier to keep the site’s content under revision control, depending on how the developer has chosen to deploy and publish to the site. This also means that content is more easily synced with code between different environments (i.e., development and production).&lt;/p&gt;

&lt;h2 id=&quot;concerns-with-static-sites&quot;&gt;Concerns with static sites&lt;/h2&gt;

&lt;p&gt;The obvious concern with static sites is that they cannot serve dynamic content. However, there are ways to add common dynamic elements to the site without the need of a full-blown CMS. For example, the popular service &lt;a href=&quot;https://disqus.com/&quot;&gt;Disqus&lt;/a&gt; provides a fully featured commenting system for a page, simply by adding a small JavaScript snippet. There are also services like Firebase that allow client-side applications to communicate directly with a database.&lt;/p&gt;

&lt;p&gt;There are, of course, many cases where a static site is inappropriate. Any service where users log in to access their content is a common example. In these cases, one might consider using a static site for blogs or landing pages and minimize the the scope of the backend code to the core of the application itself.&lt;/p&gt;

&lt;h2 id=&quot;deploying-and-publishing-content-to-static-sites&quot;&gt;Deploying and publishing content to static sites&lt;/h2&gt;

&lt;p&gt;Static sites can be deployed in a variety of ways. One of the simplest ways is to take the compiled output of the static site generator and drop it into an Amazon S3 bucket. As S3 (and similar services) are preconfigured to serve static files, there is no server setup required. Take one more step to put that bucket behind a service like Amazon CloudFront, and the site’s files are now served over CDN, with minimal effort!&lt;/p&gt;

&lt;p&gt;Another noteworthy mechanism that works great for open-source sites is &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;, a free service offered by GitHub. GitHub Pages integrates automatically with one of the most popular static site generators, Jekyll, and so deploying and pushing updates to a site is as simple as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;. &lt;a href=&quot;https://github.com/blog/1715-faster-more-awesome-github-pages&quot;&gt;GitHub Pages are also served via CDN.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are also great workflows that allow non-technical people to publish to a static site. For example, &lt;a href=&quot;https://www.contentful.com/&quot;&gt;Contentful&lt;/a&gt; is a content management service that has fast, read-only APIs for retrieving content. Non-tech writers can log into Contentful to create the content, and the static site generator could be configured to receive updates from the API, compile the site, and deploy the changes automatically. A similar workflow based on syncing a Dropbox folder of markdown files with a static site generator could also work well. With the flexibility of static sites, teams can find a publishing paradigm that works well for them.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Static websites are a practical tool in a web developer’s tool belt. The simplicity of a static website can be a welcome replacement for complex content management systems.&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 19 Sep 2015 21:41:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/static-sites-are-awesome/</guid>
    </item>
    
    <item>
      <title>Blissful Static Site Development with Jekyll, BrowserSync, and tmux</title>
      <link>https://mjswensen.com/blog/blissful-static-site-development-with-jekyll-browsersync-and-tmux/</link>
      <description>I was recently listening to the Shop Talk Show podcast where Chris Coyier shared a submission from a listener that outlined the development and deployment setup for her personal website. I thought I would add to the conversation by sharing my setup for developing mjswensen.com.</description>
      <content:encoded>&lt;p&gt;I was recently listening to the &lt;a href=&quot;http://shoptalkshow.com/&quot;&gt;Shop Talk Show podcast&lt;/a&gt; where &lt;a href=&quot;https://css-tricks.com/&quot;&gt;Chris Coyier&lt;/a&gt; shared a submission from a listener that outlined the development and deployment setup for her personal website. It involved using Dropbox to sync blog posts to a static site generator on her server and sounded pretty slick. I thought I would add to the conversation by sharing my setup for developing this site. These are the technologies that are used in concert:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub Pages&lt;/li&gt;
  &lt;li&gt;Jekyll&lt;/li&gt;
  &lt;li&gt;BrowserSync&lt;/li&gt;
  &lt;li&gt;tmux&lt;/li&gt;
  &lt;li&gt;iTerm2&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;

&lt;p&gt;Here is a visual overview of the development setup, created with &lt;a href=&quot;https://www.lucidchart.com/&quot;&gt;Lucidchart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/blissful-static-site-process.png&quot; alt=&quot;Visual representation of static site development setup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Further explanation of each component is below.&lt;/p&gt;

&lt;h2 id=&quot;github-pages&quot;&gt;GitHub Pages&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; is just about the easiest way to deploy a basic website these days. If you have a GitHub account (free), the process is simple:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a repository with a special name: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;your username&amp;gt;.github.io&lt;/code&gt; (for example, mine is &lt;a href=&quot;https://github.com/mjswensen/mjswensen.github.io&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mjswensen.github.io&lt;/code&gt;&lt;/a&gt; for this website).&lt;/li&gt;
  &lt;li&gt;Add your website files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;, etc.) to the repository.&lt;/li&gt;
  &lt;li&gt;Commit and push. Within a few minutes (usually just seconds), your site will be live at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&amp;lt;your username&amp;gt;.github.io/&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want a custom domain instead of the default, it’s easy to &lt;a href=&quot;https://help.github.com/articles/setting-up-a-custom-domain-with-github-pages/&quot;&gt;set that up&lt;/a&gt;, too.&lt;/p&gt;

&lt;p&gt;The best part? It’s &lt;em&gt;fast&lt;/em&gt;. &lt;a href=&quot;https://github.com/blog/1715-faster-more-awesome-github-pages&quot;&gt;GitHub distributes the content on GitHub Pages via CDN&lt;/a&gt; out of the box, so visitors to your website download your content quickly no matter where they are. One of my recent posts happened to be trending on &lt;a href=&quot;https://news.ycombinator.com/item?id=9113474&quot;&gt;Hacker News&lt;/a&gt; one morning and that page had about 7,500 views over the course of a few hours; the site was still responsive as ever during that time, despite the drastically increased load.&lt;/p&gt;

&lt;p&gt;And it’s &lt;strong&gt;free&lt;/strong&gt;. You really can’t beat that.&lt;/p&gt;

&lt;h2 id=&quot;jekyll&quot;&gt;Jekyll&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; is a static site generator, which—as the name suggests—makes creating static HTML sites a breeze. Jekyll is one of the most popular static site generators. This is due in part to the fact that &lt;em&gt;it integrates with GitHub Pages&lt;/em&gt;. If you build your site with Jekyll, GitHub will detect that your site is a Jekyll site and will compile it for you when you push (as outlined above). So for development, one can install Jekyll and build the site locally, push and have GitHub build the live site, and not have to worry about keeping the compiled site files in source control or uploading them via FTP.&lt;/p&gt;

&lt;p&gt;There are a lot of advantages to moving away from content management systems and toward static sites where possible. Increased site performance is the primary one (content management systems usually involve reading from a database, whereas static sites are simply files that can be quickly served from webservers like &lt;a href=&quot;http://nginx.org/&quot;&gt;nginx&lt;/a&gt; that have been heavily optimized for that task). It’s also nice to keep content under version control rather than trying to sync databases across different environments.&lt;/p&gt;

&lt;p&gt;(Sidenote: Among Jekyll’s many features, one of the most convenient is its native support for &lt;a href=&quot;http://sass-lang.com/&quot;&gt;Sass&lt;/a&gt;. Sass makes writing CSS painless and fun, and the fact that Jekyll will compile the Sass sources automatically is a big plus.)&lt;/p&gt;

&lt;h2 id=&quot;browsersync&quot;&gt;BrowserSync&lt;/h2&gt;

&lt;p&gt;While Jekyll by default has a built-in server enabled for testing as you develop, &lt;a href=&quot;http://www.browsersync.io/&quot;&gt;BrowserSync&lt;/a&gt; has some incredible features for developing for the modern web:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Live reloading when sources change&lt;/li&gt;
  &lt;li&gt;Synced page viewing and scrolling across multiple devices or browsers simultaneously&lt;/li&gt;
  &lt;li&gt;Great debugging features&lt;/li&gt;
  &lt;li&gt;Automatically go to local site in new Chrome tab on launch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, while Jekyll watches my source files for changes and generates the site automatically, BrowserSync watches Jekyll’s output directory and pushes changes to the browser automatically. It makes for a great development workflow!&lt;/p&gt;

&lt;h2 id=&quot;tmux&quot;&gt;tmux&lt;/h2&gt;

&lt;p&gt;While starting Jekyll and BrowserSync is pretty simple via the command line, it is the same two commands and requires two terminal windows each time I want to write a new blog post or develop the site. This is where &lt;a href=&quot;http://tmux.github.io/&quot;&gt;tmux&lt;/a&gt; comes in. tmux is a terminal multiplexer, which means that it allows for text-based windows and split panes within a single terminal window, among many other fantastic features.&lt;/p&gt;

&lt;p&gt;The best part about tmux is that it is easily scriptable. So for developing this site, I’ve written a Bash script that sets up my tmux session, configures the windows and panes as I like them, and starts Jekyll and BrowserSync in separate panes. The script is pretty simple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;SESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;mjswensen&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PROJECT_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Users/mjswensen/Projects/other/mjswensen.github.io&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OUTPUT_PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PROJECT_PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/_site&quot;&lt;/span&gt;

tmux new-session &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;site&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PROJECT_PATH&lt;/span&gt;

tmux split-window &lt;span class=&quot;nt&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PROJECT_PATH&lt;/span&gt;
tmux split-window &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0.1 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$OUTPUT_PATH&lt;/span&gt;

tmux send-keys &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0.0 &lt;span class=&quot;s2&quot;&gt;&quot;git status&quot;&lt;/span&gt; enter
tmux send-keys &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0.1 &lt;span class=&quot;s2&quot;&gt;&quot;jekyll build --watch&quot;&lt;/span&gt; enter
tmux send-keys &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0.2 &lt;span class=&quot;s2&quot;&gt;&quot;browser-sync start --server --files &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;*.css, *.html&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; enter

tmux &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-pane&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;:0.0

tmux &lt;span class=&quot;nt&quot;&gt;-CC&lt;/span&gt; attach-session &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SESSION&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;iterm2&quot;&gt;iTerm2&lt;/h2&gt;

&lt;p&gt;Finally, I use &lt;a href=&quot;http://iterm2.com/&quot;&gt;iTerm2&lt;/a&gt; in place of the standard Terminal.app for Mac. iTerm2 has some amazing features and is a popular choice for terminal emulation among Mac users. The reason I like iTerm2 so much is for its deep tmux integrations. While tmux brings some great features to the terminal, certain common tasks—like switching between windows or copying text, or scrolling—involve cumbersome keyboard commands and workarounds. iTerm2, when attached to a tmux session with the special &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-CC&lt;/code&gt; command-line flag (as seen in the script above), &lt;em&gt;converts tmux windows and panes to native iTerm2 tabs and panes&lt;/em&gt;. It sounds simple, but it really makes all the difference. It really is a great solution for tmux fans.&lt;/p&gt;

&lt;p&gt;In the end, this is what the iTerm2 window looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/blissful-static-site-iterm2-screenshot.png&quot; alt=&quot;Screenshot of iTerm2 connected to tmux session&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 20 Jun 2015 05:44:34 +0000</pubDate>
      <guid>https://mjswensen.com/blog/blissful-static-site-development-with-jekyll-browsersync-and-tmux/</guid>
    </item>
    
    <item>
      <title>Key Takeaways to Common CSS Questions</title>
      <link>https://mjswensen.com/blog/key-takeaways-to-common-css-questions/</link>
      <description>One of my goals for Q2 2015 was to answer ten CSS-related questions on Stack Overflow. These are some of the key concepts I came across while doing so.</description>
      <content:encoded>&lt;p&gt;One of my goals for Q2 2015 was to answer ten CSS-related questions on Stack Overflow. Below is an outline of some of the key concepts I came across while doing so.&lt;/p&gt;

&lt;h2 id=&quot;preprocessor-compilation&quot;&gt;Preprocessor Compilation&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/questions/29472923/sass-nesting-at-2-levels/29473205&quot;&gt;This question&lt;/a&gt; regarded the relative “efficiency” of two comparable snippets of Sass code. What the original poster did not realize is that the differences were purely preferential, and that the two examples in fact compiled down to the exact same CSS code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Understanding how preprocessors behave is important for producing quality code. The best way to know what the compiled output of a preprocessor is to go back to &lt;a href=&quot;https://mjswensen.github.io/css-power-ups/introduction-and-selectors/#/3&quot;&gt;the basics of CSS selectors&lt;/a&gt;, specifically compounding simple selectors (and sequences of simple selectors) together with &lt;a href=&quot;https://mjswensen.github.io/css-power-ups/introduction-and-selectors/#/7&quot;&gt;combinators&lt;/a&gt;. Understanding combinators is the key to effectively using the popular preprocessors’ nesting and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; syntax.&lt;/p&gt;

&lt;h2 id=&quot;parent-selector&quot;&gt;Parent Selector&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;http://stackoverflow.com/questions/29528157/nested-div-css-selector/29528204&quot;&gt;this question&lt;/a&gt;, the original poster was on a wild goose chase that most CSS developers have found themselves on at one point or another: trying to select a parent element based on whether or not it contains a given descendant. For example, all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;section&lt;/code&gt;s that contain an element with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metadata&lt;/code&gt; class, but not those that do not (proposed syntax: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;section &amp;lt; .metadata&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Unfortunately, the parent selector does not yet exist, but may someday in &lt;a href=&quot;http://www.w3.org/TR/selectors4/&quot;&gt;a future CSS specification&lt;/a&gt;. There may be, however, alternate solutions to solving that problem, such as the &lt;a href=&quot;https://mjswensen.com/blog/2015/05/11/practical-use-cases-for-the-empty-pseudo-class/&quot;&gt;use of psuedo-classes like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:empty&lt;/code&gt;&lt;/a&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-box-model&quot;&gt;The Box Model&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;http://stackoverflow.com/questions/29753912/figcaption-not-aligning-correctly-under-image/29753979&quot;&gt;this question&lt;/a&gt;, the original poster was tripped up on how an element’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;padding&lt;/code&gt; affects its position and size—a very common issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; A lack of understanding for &lt;a href=&quot;https://mjswensen.github.io/css-power-ups/the-box-model-and-positioning/#/&quot;&gt;the CSS box model&lt;/a&gt; is a common source of error for layout and alignment concerns. Also, the use of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;box-sizing&lt;/code&gt; property can greatly simplify the reasoning behind an element’s computed size.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/key-takeaways-box-model.jpg&quot; alt=&quot;The CSS box model&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-display-and-visibility-properties&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt; And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visibility&lt;/code&gt; Properties&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;http://stackoverflow.com/questions/30766049/css3-animations-being-triggered-on-display-change/30766624&quot;&gt;this question&lt;/a&gt;, the original poster discovered that changing an element’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt; property from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;none&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt; re-triggered any animations for that element. The solution was to toggle between the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hidden&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visible&lt;/code&gt; values of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visibility&lt;/code&gt; property instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Animations do not play nicely with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display: none;&lt;/code&gt; in general. The use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visibility: hidden;&lt;/code&gt; can sometimes help with this, but special care must be taken as there are differences in behavior and styling between these two property-value pairs. (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visibility: hidden;&lt;/code&gt; maintains an element’s space in the flow of the web page while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display: none;&lt;/code&gt; removes it from the flow. For this reason, in certain circumstances there will be &lt;a href=&quot;http://csstriggers.com/#display&quot;&gt;performance gains from favoring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;visibility&lt;/code&gt;&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/key-takeaways-visibility-display.jpg&quot; alt=&quot;visibility: hidden vs. display: none&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;svg&quot;&gt;SVG&lt;/h2&gt;

&lt;p&gt;SVG is an extremely powerful and open graphics format. It is closely related to HTML in that both are XML (SVG images are therefore quite compatible with web pages) and CSS in that SVG elements can be styled with CSS, just like HTML elements. The original poster of &lt;a href=&quot;http://stackoverflow.com/questions/30784486/svg-mutiple-images-with-different-behaviors/30786215&quot;&gt;this question&lt;/a&gt; was simply not familiar with some of the SVG features used in the code. In this case, adding a class to the SVG element (and therefore being able to make &lt;a href=&quot;https://mjswensen.github.io/css-power-ups/the-cascade-and-specificity/#/2&quot;&gt;specificity&lt;/a&gt; work to our advantage) was enough to accomplish the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; While there are plenty of graphical tools and libraries for manipulating SVG images, being comfortable in the source code of an SVG can be valuable. Some of the best references for SVG are found on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG&quot;&gt;Mozilla Developer Network site&lt;/a&gt;. &lt;a href=&quot;http://sarasoueidan.com/&quot;&gt;Sara Soueidan&lt;/a&gt; also has a lot of great content and research around SVG.&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 12 Jun 2015 14:42:13 +0000</pubDate>
      <guid>https://mjswensen.com/blog/key-takeaways-to-common-css-questions/</guid>
    </item>
    
    <item>
      <title>Practical Use Cases For The :empty Pseudo-class</title>
      <link>https://mjswensen.com/blog/practical-use-cases-for-the-empty-pseudo-class/</link>
      <description>At first, the :empty pseudo-class may not seem to be the most useful of the advanced selectors. However, there are situations in which :empty is the perfect tool for the job.</description>
      <content:encoded>&lt;p&gt;At first, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:empty&lt;/code&gt; pseudo-class may not seem to be the most useful of the advanced selectors. However, there are situations in which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:empty&lt;/code&gt; is the perfect tool for the job. Here are a couple of examples:&lt;/p&gt;

&lt;h2 id=&quot;taglabel-manager-ui&quot;&gt;Tag/label manager UI&lt;/h2&gt;

&lt;p&gt;Say you were developing an application that allowed users to manage tags or labels (think &lt;a href=&quot;https://trello.com/&quot;&gt;Trello&lt;/a&gt;). It would be nice to allow users to add titles to their labels, but the labels should also be visually sufficient if no titles are added, like so:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/css-empty-labels.png&quot; alt=&quot;Tag/label manager UI&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You’ll notice that the tags without text are wider than they would be if they were simply an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;span&amp;gt;&lt;/code&gt; with padding and a background color. The essentials of the code to achieve this is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nc&quot;&gt;.label&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:empty&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Simple, yet adds a bit of robustness to the design. I made the labels &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contenteditable&lt;/code&gt; and &lt;a href=&quot;http://codepen.io/mjswensen/pen/jPWrNe&quot;&gt;put it up on CodePen&lt;/a&gt; so that it can be played with:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://codepen.io/mjswensen/pen/jPWrNe&quot;&gt;&lt;img src=&quot;/blog/images/css-empty-contenteditable.gif&quot; alt=&quot;Editable tags example&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;analytics-dashboard-ui&quot;&gt;Analytics dashboard UI&lt;/h2&gt;

&lt;p&gt;Another example use case would be a data collection user interface that hasn’t received any data yet. Suppose an application was tracking the conversion rate of an e-commerce site by geographical region, like so:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/css-empty-conversions.png&quot; alt=&quot;Data collection UI example&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the previous example, the elements for the USA and Canada metrics have text (“1.64%” and “0.53%”, respectively), while that of Mexico is empty. This is the relevant code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nc&quot;&gt;.conversion-rate&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:empty::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;No data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;http://codepen.io/mjswensen/pen/jPWrNe&quot;&gt;Here is a link to the live example.&lt;/a&gt; It’s a nice little trick that provides a better experience than simply leaving the space blank in the absence of data.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;As web professionals, we take pride in making sure our designs look great no matter the circumstances—think &lt;em&gt;responsive design&lt;/em&gt;, for example—and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:empty&lt;/code&gt; pseudo-class is another tool in our tool box to achieve that.&lt;/p&gt;

&lt;p&gt;What other great use cases for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:empty&lt;/code&gt; have you come up with?&lt;/p&gt;
</content:encoded>
      <pubDate>Tue, 12 May 2015 04:17:54 +0000</pubDate>
      <guid>https://mjswensen.com/blog/practical-use-cases-for-the-empty-pseudo-class/</guid>
    </item>
    
    <item>
      <title>The Power of Education: Truly Thinking</title>
      <link>https://mjswensen.com/blog/the-power-of-education-truly-thinking/</link>
      <description>We have the power to truly think, to choose what has meaning and what doesn't in the mundane moments of everyday life.</description>
      <content:encoded>&lt;p&gt;I very recently graduated from Brigham Young University with a master’s degree in Information Systems Management. In one of my final class sessions, a professor showed us this video and asked us to reflect on what our education means to us. As the video points out, we have the power to &lt;em&gt;truly think&lt;/em&gt;, to choose what has meaning and what doesn’t in the mundane moments of everyday life. The video is made from a speech given ten years ago, but the message is a timeless gem.&lt;/p&gt;

&lt;iframe src=&quot;https://player.vimeo.com/video/68855377&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

</content:encoded>
      <pubDate>Mon, 27 Apr 2015 20:42:18 +0000</pubDate>
      <guid>https://mjswensen.com/blog/the-power-of-education-truly-thinking/</guid>
    </item>
    
    <item>
      <title>How Personal Revelation Helps Us Prepare For Eternal Life</title>
      <link>https://mjswensen.com/blog/how-personal-revelation-helps-us-prepare-for-eternal-life/</link>
      <description>Heavenly Father is closer to us than we realize. He wants to communicate with us and has given us the pattern by which we can ask Him for help.</description>
      <content:encoded>&lt;p&gt;I had the opportunity to give a talk on this topic to a congregation of &lt;a href=&quot;https://www.churchofjesuschrist.org/?lang=eng&quot;&gt;the Church of Jesus Christ&lt;/a&gt; in Provo, UT. The following is the transcript.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Good morning! My name is Matt Swensen. Today I will be talking about how personal revelation helps us prepare for eternal life. (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/bd/revelation.p2?lang=eng&amp;amp;letter=r&quot;&gt;Personal revelation is a process by which God communicates with us directly, leading and guiding our lives.&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I will start by sharing with you a weakness that I am working through. I tend to rely upon my own capacities more than I rely on the Lord, especially when it comes to solving problems or dealing with trials. What I have learned while preparing for this talk is that while our efforts to move forward and learn from our own experiences are an important part of our purpose—I’m not saying that we shouldn’t try to solve our own problems—learning how to rely on the Lord and follow personal revelation is even more important.&lt;/p&gt;

&lt;p&gt;This is what then-Elder Packer (now President Packer) said about it in &lt;a href=&quot;https://www.churchofjesuschrist.org/general-conference/1994/10/personal-revelation-the-gift-the-test-and-the-promise?lang=eng&quot;&gt;an October 1994 General Conference address&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Physically you can see with eyes and hear with ears and touch and feel and learn. Through your intellect, you learn most of what you know about the world in which we live…&lt;/p&gt;

  &lt;p&gt;But if you learn by reason only, you will never understand the Spirit and how it works—regardless of how much you learn about other things…&lt;/p&gt;

  &lt;p&gt;Your spirit learns in a different way than does your intellect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And &lt;a href=&quot;https://www.churchofjesuschrist.org/general-conference/2012/04/how-to-obtain-revelation-and-inspiration-for-your-personal-life?lang=eng&quot;&gt;Elder Scott, in the April 2012 General Conference&lt;/a&gt;, said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our Heavenly Father did not put us on earth to fail but to succeed gloriously… Sometimes we unwisely try to face life by depending on our own experience and capacity. It is much wiser for us to seek through prayer and divine inspiration to know what to do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Brothers and sisters, I know that we will be better off if we rely more on the Lord than on our own intellect. We can do this by seeking personal revelation from the Lord.&lt;/p&gt;

&lt;p&gt;Personal revelation helps us prepare for eternal life because we learn how to follow the will of God, and therefore become one with Him. The Savior outlined this type of unity in &lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/nt/john/17.20-23?lang=eng&quot;&gt;His great Intercessory Prayer in the 17th chapter of John&lt;/a&gt;. Keep in mind that when He refers to “these” or “they,” He is referring to us—recipients of personal revelation. He said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Neither pray I for these alone, but for them also which shall believe on me through their word;&lt;/p&gt;

  &lt;p&gt;That they all may be one; as thou, Father, art in me, and I in thee, that they also may be one in us: that the world may believe that thou hast sent me.&lt;/p&gt;

  &lt;p&gt;And the glory which thou gavest me I have given them; that they may be one, even as we are one:&lt;/p&gt;

  &lt;p&gt;I in them, and thou in me, that they may be made perfect in one; and that the world may know that thou hast sent me, and hast loved them, as thou hast loved me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Put another way, eternal life is being one with God. Personal revelation helps us achieve that.&lt;/p&gt;

&lt;p&gt;When we communicate with others, we get to know them better. The same is true with our communication with God. Personal revelation helps us know His will, His wisdom, and His personality. The concept of knowing God is an important part of our faith, as outlined in &lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/nt/john/17.3?lang=eng&quot;&gt;verse three of the same chapter of John&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;And this is life eternal, that they might know thee the only true God, and Jesus Christ, whom thou hast sent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, personal revelation helps us prepare for eternal life because it helps us know God.&lt;/p&gt;

&lt;p&gt;I have divided the rest of my talk into three short topics: (1) how to receive personal revelation, (2) the blessings of receiving personal revelation, and (3) the importance of recording personal revelation when we receive it.&lt;/p&gt;

&lt;h2 id=&quot;how-to-receive-personal-revelation&quot;&gt;How to Receive Personal Revelation&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;https://www.churchofjesuschrist.org/general-conference/1994/10/personal-revelation-the-gift-the-test-and-the-promise?lang=eng&quot;&gt;the aforementioned talk&lt;/a&gt;, Elder Packer outlines the process by which we can elicit personal revelation. He breaks it down clearly into two basic steps.&lt;/p&gt;

&lt;h3 id=&quot;prayer&quot;&gt;Prayer&lt;/h3&gt;

&lt;p&gt;The first is prayer. We must use our agency (freedom of choice) to pray and ask. Elder Packer points out in his talk that the invitation to “ask and ye shall receive” (&lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/nt/john/16.24?lang=eng&quot;&gt;John 16:24&lt;/a&gt;) is one of the most common commandments in the scriptures. We need to humbly go before the Father in prayer and lay out our questions, asking for His divine help.&lt;/p&gt;

&lt;h3 id=&quot;study&quot;&gt;Study&lt;/h3&gt;

&lt;p&gt;After we pray, we must study the issue. The Lord expects us to put forth effort before He will give us the answer. Sometimes He answers by directing our study to find what we need. Usually, this means turning to the scriptures or to the words of the modern-day prophets.&lt;/p&gt;

&lt;h3 id=&quot;receiving-the-revelation&quot;&gt;Receiving the Revelation&lt;/h3&gt;

&lt;p&gt;The Lord will then communicate with us, according to His will and timeline. Elder Packer points out that when we feel the answers come, it is primarily a spiritual feeling. Occasionally the spiritual light is accompanied by physical sensations and emotional feelings (like warmth and peace).&lt;/p&gt;

&lt;h3 id=&quot;a-personal-experience&quot;&gt;A Personal Experience&lt;/h3&gt;

&lt;p&gt;One of the most distinct personal revelations I have received in my life was when I knew I was to marry Christine. We had been dating and communicating long-distance for a few months, and I had come to know that she embodied everything that was important to me in an eternal companion. I knelt in prayer, asking Heavenly Father if it was in line with His will that we be married. My mind and spirit filled with light and some specific words from my patriarchal blessing came to my mind—as if I had been looking at them on the page—affirming to me that I should move forward.&lt;/p&gt;

&lt;h2 id=&quot;the-blessings-of-receiving-personal-revelation&quot;&gt;The Blessings of Receiving Personal Revelation&lt;/h2&gt;

&lt;p&gt;In addition to the blessing of preparing for eternal life, there are many other blessings that come with personal revelation.&lt;/p&gt;

&lt;h3 id=&quot;increased-confidence-in-gods-love-for-us-and-in-his-caring-for-us&quot;&gt;Increased Confidence in God’s Love For Us and in His Caring For Us&lt;/h3&gt;

&lt;p&gt;For example, when Heavenly Father hears and answers our prayers, we gain an increase in our confidence that God loves us and is actively watching over us. We feel His love and we feel closer to Him. Our testimony that he is indeed our Father is strengthened. In &lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/bofm/3-ne/14.7-11&quot;&gt;the 14th chapter of 3rd Nephi&lt;/a&gt;, the Savior echoed to the Nephites the words he once gave to his disciples in His Sermon on the Mount:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ask, and it shall be given unto you; seek, and ye shall find; knock, and it shall be opened unto you.&lt;/p&gt;

  &lt;p&gt;For every one that asketh, receiveth; and he that seeketh, findeth; and to him that knocketh, it shall be opened.&lt;/p&gt;

  &lt;p&gt;Or what man is there of you, who, if his son ask bread, will give him a stone?&lt;/p&gt;

  &lt;p&gt;Or if he ask a fish, will he give him a serpent?&lt;/p&gt;

  &lt;p&gt;If ye then, being evil, know how to give good gifts unto your children, how much more shall your Father who is in heaven give good things to them that ask him?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;strength-to-move-forward&quot;&gt;Strength to Move Forward&lt;/h3&gt;

&lt;p&gt;When we act upon the revelation we receive, we have more strength to move forward even when the way is difficult. This is because we know that we are in line with God’s will and we trust in His power to do all things. We are not aimlessly wandering, hoping that we made the right choice or are following the right path; we are progressing with confidence toward achieving eternal goals and becoming more like our Heavenly Father.&lt;/p&gt;

&lt;h3 id=&quot;the-importance-of-recording-personal-revelation-when-we-receive-it&quot;&gt;The Importance of Recording Personal Revelation When We Receive It&lt;/h3&gt;

&lt;p&gt;This strength and confidence relates directly to my last topic: the importance of recording personal revelation when we receive it. Christine and I have experienced this first-hand. As many of you have discovered or will discover, growing your family with children is a singular blessing that brings its own unique challenges. During those challenges, Christine and I have often looked back to the specific experiences when we received direction from the Lord regarding our family. Being able to read our own words about those sacred experiences has given us great strength.&lt;/p&gt;

&lt;p&gt;I invite all of us to make a habit of always writing down our spiritual impressions. We will be so glad we did.&lt;/p&gt;

&lt;p&gt;Not only will our journals give us strength in the days ahead, but they can benefit our future generations. When I gave a talk in this ward last year I shared a journal entry from one of my ancestors and how it strengthened my testimony. I have had several more of similar experiences since because of the diligence of my ancestors in writing in their journals.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Brothers and sisters, I know that Heavenly Father is there. I think that He is closer to us than we realize. He wants to communicate with us and has given us the pattern by which we can ask Him for help. I know that He loves us. In the name of Jesus Christ, amen.&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 10 Apr 2015 13:30:19 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-personal-revelation-helps-us-prepare-for-eternal-life/</guid>
    </item>
    
    <item>
      <title>Why Code Snobs Are Invaluable</title>
      <link>https://mjswensen.com/blog/why-code-snobs-are-invaluable/</link>
      <description>Some argue that "code snobs" waste time on trivia. While I am sure this can be the case, I submit that their ideas and comments offer more benefit than cost in the long run.</description>
      <content:encoded>&lt;p&gt;Some argue that “code snobs” waste time on trivia. While I am sure this can be the case, I submit that their ideas and comments offer more benefit than cost in the long run.&lt;/p&gt;

&lt;p&gt;I recently pushed a change at work that involved refactoring a few of our JavaScript classes and adding a couple of new ones. Nothing too out of the ordinary. Our development workflow requires a different engineer to code review any commits before they go on to the master branch (and eventually out to production). I felt good about the solution that I had come up with for the particular task I was working on.&lt;/p&gt;

&lt;p&gt;The engineer that I had requested for the code review—who had recently reworked our &lt;a href=&quot;http://www.seleniumhq.org/&quot;&gt;Selenium&lt;/a&gt; testing framework and is an active proponent of code quality for our team—left a comment on our pull request system for a particular area of the code I was submitting. He suggested a slight change, merely that some of the classes I had implemented were not using the singleton pattern, while some of their analogous counterparts were, and that I should adjust them to better mirror that structure.&lt;/p&gt;

&lt;p&gt;I was halfway through replying to the comment with reasons to just move forward and approve my pull request anyway when I realized that he was right. The classes really &lt;em&gt;would&lt;/em&gt; be more semantic if they were singletons. I decided to take the additional 15 minutes to make the change.&lt;/p&gt;

&lt;p&gt;I realize that whether or not my two small JavaScript classes were implemented as singletons probably has no real bearing on the reliability and performance of our large codebase. &lt;strong&gt;But small decisions like that add up, especially when they are made every day.&lt;/strong&gt; Imagine how different your codebase would be if every programmer on your team pushed out the best quality code they could, every single commit, with unfaltering snobbery! Over the course of even a year, the difference would be significant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perhaps a “code snob” is simply the term lazy programmers use to describe their more disciplined peers.&lt;/strong&gt; If that’s the case, I want to be one.&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 30 Jan 2015 20:50:44 +0000</pubDate>
      <guid>https://mjswensen.com/blog/why-code-snobs-are-invaluable/</guid>
    </item>
    
    <item>
      <title>How Well Do You Know Your Programming Language?</title>
      <link>https://mjswensen.com/blog/how-well-do-you-know-your-programming-language/</link>
      <description>I was listening to a podcast about Scala by Scott Hanselman and Dianne Marsh where programming languages were briefly compared to foreign languages. The idea stuck in my mind for the rest of the drive to work.</description>
      <content:encoded>&lt;p&gt;I was listening to &lt;a href=&quot;http://hanselminutes.com/394/demystifying-scala-with-netflixs-dianne-marsh&quot;&gt;a podcast about Scala&lt;/a&gt; by &lt;a href=&quot;http://www.hanselman.com/&quot;&gt;Scott Hanselman&lt;/a&gt; and &lt;a href=&quot;http://diannemarsh.com/&quot;&gt;Dianne Marsh&lt;/a&gt; where programming languages were briefly compared to foreign languages. The idea stuck in my mind for the rest of the drive to work. I came up with the following scale that can be used to help self-assess one’s proficiency with a programming language.&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Level&lt;/th&gt;
        &lt;th&gt;Programming Language&lt;/th&gt;
        &lt;th&gt;Foreign Language&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;1 (inexperienced)&lt;/td&gt;
        &lt;td&gt;You don&#39;t recognize the syntax, either because the language utilizes an unfamiliar paradigm or is from a different family of languages than what you are used to. The file extension might provide a hint—depending on how good your memory is.&lt;/td&gt;
        &lt;td&gt;You might be able to guess which continent this language is used in, but you wouldn&#39;t feel comfortable taking a stab at identifying the language with anyone listening.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;You know what language it is, but the syntax feels quite uncomfortable (or even unintelligible).&lt;/td&gt;
        &lt;td&gt;You are able to identify the language based on the look of the alphabet, but phonetics, grammar, and general structure are still very foreign.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;3&lt;/td&gt;
        &lt;td&gt;You understand the context in which the language resides and are able to hack together basic functionality by copying and pasting snippets from Stack Overflow.&lt;/td&gt;
        &lt;td&gt;You have memorized a few basic phrases for ordering food and asking where the bathroom is.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;4&lt;/td&gt;
        &lt;td&gt;You can make things work, but the level of code quality is such that it pains others (and future you) to read. You don&#39;t yet enjoy writing code in the new language.&lt;/td&gt;
        &lt;td&gt;Though still fragmented, you have a basic knowledge of the structure of the language and can form a basic sentence on the fly, albeit grammatically unsound.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;5&lt;/td&gt;
        &lt;td&gt;You write code at an acceptable level. It&#39;s no longer completely written in the paradigms of languages you are more comfortable with. You are starting to feel comfortable in the new language.&lt;/td&gt;
        &lt;td&gt;Though you are still simply speaking your native language using foreign words some of the time, you are able to carry on a conversation.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;6&lt;/td&gt;
        &lt;td&gt;You are adopting more and more of the idioms of the new language. You are embracing the best practices and avoiding the pitfalls and code smells of the new ecosystem that is becoming more familiar to you by the day. You creatively solve problems in the way that only the new language would.&lt;/td&gt;
        &lt;td&gt;You are starting to think and dream in your new language. You wish there were ways to express certain ideas in your native language that are only achievable in the foreign language.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;7 (proficient)&lt;/td&gt;
        &lt;td&gt;Writing code the idiomatic way for the new language is natural and comfortable. Reading quality work by others is easy and pleasant. The new language has become your go-to for thinking through problems. You even contribute to the community and help spread enthusiasm for the language.&lt;/td&gt;
        &lt;td&gt;Your command of the new language is such that you understand the subtle nuances of poetry. You are even capable of writing poetry yourself, and occasionally come up with something brilliant, indistinguishable from that of a native speaker of the language.&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Where you do fall on the scale for the languages you work in every day?&lt;/p&gt;
</content:encoded>
      <pubDate>Sat, 16 Aug 2014 05:48:17 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-well-do-you-know-your-programming-language/</guid>
    </item>
    
    <item>
      <title>Configurations for a Friendlier tmux Status Bar</title>
      <link>https://mjswensen.com/blog/friendlier-tmux-status-bar/</link>
      <description>I recently added a few configuration options to my local tmux configuration file to make the status bar easier to read.</description>
      <content:encoded>&lt;p&gt;At work, the engineering team uses &lt;a href=&quot;https://tmux.github.io/&quot;&gt;tmux&lt;/a&gt; to manage the several instances of the services we run to develop the Lucid suite locally. It is a phenomenal terminal management system and the best part is that it is highly configurable. If you enjoy working in the command-line and haven’t checked it out yet, you need to!&lt;/p&gt;

&lt;p&gt;This is what the default status bar looks like with a few windows open:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/tmux-status-bar-before.png&quot; alt=&quot;tmux status bar before configurations&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I added a few config options to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.tmux.conf&lt;/code&gt; file to make the status bar easier to read:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-left &lt;span class=&quot;s2&quot;&gt;&quot; #S &quot;&lt;/span&gt;
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-left-fg black
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-left-bg white

set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-right &lt;span class=&quot;s2&quot;&gt;&quot; %H:%M %d-%b-%y &quot;&lt;/span&gt;
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-right-fg black
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; status-right-bg white

set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; window-status-format &lt;span class=&quot;s2&quot;&gt;&quot; #I: #W &quot;&lt;/span&gt;

set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; window-status-current-format &lt;span class=&quot;s2&quot;&gt;&quot; #I: #W &quot;&lt;/span&gt;
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; window-status-current-fg green
set-window-option &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; window-status-current-bg black&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is what the status bar looks like now:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/tmux-status-bar-after.png&quot; alt=&quot;tmux status bar after configurations&quot; /&gt; (Terminal theme is the beautiful &lt;a href=&quot;https://github.com/chriskempson/tomorrow-theme&quot;&gt;Tomorrow Theme&lt;/a&gt; by &lt;a href=&quot;http://chriskempson.com&quot;&gt;Chris Kempson&lt;/a&gt;.)&lt;/p&gt;

&lt;div class=&quot;cards updates&quot;&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;span class=&quot;card-title&quot;&gt;Update 5/16/2015&lt;/span&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;I still love and use tmux every day. I have recently moved from the default Terminal.app to the fantastic &lt;a href=&quot;http://iterm2.com/&quot;&gt;iTerm2.app&lt;/a&gt;, in part because it has wonderful tmux integrations. In a nutshell, it is possible to attach to a tmux session with a special command-line flag that uses native iTerm panes/tabs/windows in place of the tmux panes and windows. This allows for easier scrolling, better copy/pasting, and better mouse integrations (i.e., switching between panes). It has loads of other features as well. I can&#39;t recommend it enough.&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;card&quot;&gt;
    &lt;span class=&quot;card-title&quot;&gt;Update 8/26/2017&lt;/span&gt;
    &lt;div class=&quot;card-body&quot;&gt;
      &lt;p&gt;It&#39;s been just over three years since I wrote this post, and I still use tmux every day. My custom tmux status bar theme has largely remained the same as outlined above, too. For the rest of my development environment, I recently built a tool called &lt;a href=&quot;https://github.com/mjswensen/themer&quot;&gt;&lt;code&gt;themer&lt;/code&gt;&lt;/a&gt; that greatly simplifies keeping a consistent theme between editors, terminals, other apps, and even desktop wallpaper. &lt;a href=&quot;https://themer.dev&quot;&gt;Check out &lt;code&gt;themer&lt;/code&gt; here.&lt;/a&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</content:encoded>
      <pubDate>Sat, 09 Aug 2014 23:22:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/friendlier-tmux-status-bar/</guid>
    </item>
    
    <item>
      <title>Options For Git Status in Shell Prompt</title>
      <link>https://mjswensen.com/blog/git-status-prompt-options/</link>
      <description>When it comes to customizing your Bash prompt to give you more information about the status of a Git repository, there are a lot of options out there. This post covers using the built-in options that ship with Git.</description>
      <content:encoded>&lt;style&gt;
:root {
  --meta-color: var(--light-shade5);
}
@media (prefers-color-scheme: dark) {
  :root {
    --meta-color: var(--dark-shade5);
  }
}

.meta {
  color: var(--meta-color);
  text-align: center;
  font-style: italic;
}
&lt;/style&gt;

&lt;p&gt;When it comes to customizing your Bash prompt to give you more information about the status of a Git repository, there are a lot of options out there. This post covers using the built-in options that ship with Git, as opposed to a third-party alternatives like &lt;a href=&quot;https://github.com/magicmonty/bash-git-prompt&quot;&gt;this&lt;/a&gt; or &lt;a href=&quot;https://github.com/lvv/git-prompt&quot;&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once Git is installed, all you need to do is:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Load the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git-prompt.sh&lt;/code&gt; file (comes with Git) in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bashrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.profile&lt;/code&gt; file&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(__git_ps1)&lt;/code&gt; somewhere in your bash prompt variable (look for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PS1&lt;/code&gt; in that same files)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The current branch name will always be displayed. However, there are a few other options you can use to add more than just the current branch name to your prompt. The available configurations are not easily Google-able, so I sifted through the &lt;a href=&quot;https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh&quot;&gt;source&lt;/a&gt; and listed them here. The available options are:&lt;/p&gt;

&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Option&lt;/th&gt;
        &lt;th&gt;Value&lt;/th&gt;
        &lt;th&gt;Description&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;GIT_PS1_SHOWDIRTYSTATE&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;any nonempty value&lt;/td&gt;
        &lt;td&gt;shows &lt;code&gt;*&lt;/code&gt; or a &lt;code&gt;+&lt;/code&gt; for unstaged and staged changes, respectively&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;GIT_PS1_SHOWSTASHSTATE&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;any nonempty value&lt;/td&gt;
        &lt;td&gt;shows &lt;code&gt;$&lt;/code&gt; if there are any stashes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;GIT_PS1_SHOWUNTRACKEDFILES&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;any nonempty value&lt;/td&gt;
        &lt;td&gt;shows &lt;code&gt;%&lt;/code&gt; if there are any untracked files&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td rowspan=&quot;7&quot;&gt;&lt;code&gt;GIT_PS1_SHOWUPSTREAM&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;shows &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;, or &lt;code&gt;=&lt;/code&gt; when your branch is behind, ahead, diverged from, or in sync with the upstream branch, respectively&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td colspan=&quot;2&quot; class=&quot;meta&quot;&gt;(alternatively to &lt;code&gt;auto&lt;/code&gt;, a space-delimited list of the following options)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;verbose&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;show number of commits ahead/behind (+/-) upstream&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;if verbose, then also show the upstream abbrev name&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;legacy&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;don&#39;t use the &#39;--count&#39; option available in recent versions of git-rev-list&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;git&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;always compare HEAD to @{upstream}&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;svn&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;always compare HEAD to your SVN upstream&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td rowspan=&quot;5&quot;&gt;&lt;code&gt;GIT_PS1_DESCRIBE_STYLE&lt;/code&gt;&lt;/td&gt;
        &lt;td colspan=&quot;2&quot; class=&quot;meta&quot;&gt;(determines the style of the current commit when in a detached HEAD state, when set to one of the following values)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;contains&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;relative to newer annotated tag (e.g., &lt;code&gt;v1.6.3.2~35&lt;/code&gt;)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;branch&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;relative to newer tag or branch (e.g., &lt;code&gt;master~4&lt;/code&gt;)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;describe&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;relative to older annotated tag (e.g., &lt;code&gt;v1.6.3.1-13-gdd42c2f&lt;/code&gt;)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;exactly matching tag&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;Here’s how you might put it all together in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bashrc&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.profile&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# First, load up the git-prompt.sh file that ships with Git. You&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# might consider copying it to your home folder first. On my Mac&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# OS X (Mavericks) setup, the file resides in&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# /Applications/Xcode.app/Contents/Developer/usr/share/git-core/.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/git-prompt.sh &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; ~/git-prompt.sh

&lt;span class=&quot;c&quot;&gt;# Then, set the options as desired. Remember that the current&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# branch name is displayed no matter what.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_PS1_SHOWSTASHSTATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true
export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_PS1_SHOWDIRTYSTATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true
export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_PS1_SHOWUNTRACKEDFILES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true
export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_PS1_SHOWUPSTREAM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;auto&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Finally, add $(__git_ps1) to the prompt variable. That&#39;s it!&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PS1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\u&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\h&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\w\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;(__git_ps1) &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here is how the prompt might look:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/git-prompt.png&quot; alt=&quot;Git status embedded in bash prompt&quot; /&gt;&lt;/p&gt;
</content:encoded>
      <pubDate>Fri, 08 Aug 2014 04:01:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/git-status-prompt-options/</guid>
    </item>
    
    <item>
      <title>How the Gospel of Christ Relates to Those Who Passed On Without It</title>
      <link>https://mjswensen.com/blog/how-the-gospel-of-christ-relates-to-those-who-passed-on-without-it/</link>
      <description>The Plan of Salvation is a selfless plan of love.</description>
      <content:encoded>&lt;p&gt;I had the opportunity to give a talk on this topic to a congregation of &lt;a href=&quot;https://www.churchofjesuschrist.org/?lang=eng&quot;&gt;the Church of Jesus Christ&lt;/a&gt; in Orem, UT. The following is the transcript.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Brothers and sisters, can you think of a time when you were in a sticky situation? A situation where you needed to be in two places at once, or where you just couldn’t get somewhere fast enough? Or you just couldn’t do something by yourself? Were you able to call a close friend or family member to help? Do you remember the relief you felt as they came to the rescue? Do you remember how nice it was to have the weight lift off your shoulders as everything worked itself out? Do you remember how good it felt to have that friend or family member cheerfully respond to your plea for help, even though you knew it wasn’t convenient for them?&lt;/p&gt;

&lt;p&gt;I have the opportunity to speak today about one of the great messages of hope and rescue that the gospel brings. It’s an aspect of the gospel that no other church has. It also sometimes gets overlooked in our church and is one that I personally take for granted. The topic is family history and performing ordinances vicariously for those that have already passed on. It fits perfectly with what Courtney and Cedar just spoke about: families and love.&lt;/p&gt;

&lt;p&gt;For those of you who are visiting with us today or who are unfamiliar with family history, it is the process of gathering information about our ancestors and finding out who they are so that we can vicariously perform sacred ordinances in temples as proxies for them. These ordinances allow them to enjoy the blessings of the gospel in the afterlife even though they didn’t have an opportunity to receive them when they were alive.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.churchofjesuschrist.org/manual/teachings-joseph-smith/chapter-41&quot;&gt;The Prophet Joseph Smith said&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;…suppose the case of two men, brothers, equally intelligent, learned, virtuous and lovely, walking in uprightness and in all good conscience, so far as they have been able to discern duty from the muddy stream of tradition, or from the blotted page of the book of nature.&lt;/p&gt;

  &lt;p&gt;One [of them] dies and is buried, having never heard the Gospel of reconciliation; to the other the message of salvation is sent, he hears and embraces it, and is made the heir of eternal life. Shall the one become the partaker of glory and the other be consigned to hopeless perdition? Is there no chance for his escape?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Outlined here is one of the great questions of life: is there no hope for those who pass away without having had a chance to embrace the gospel of Jesus Christ? Joseph explains that there &lt;em&gt;is&lt;/em&gt; a way for this first brother, and that it is through the ordinances performed by proxy in the temple. Though God is a just God in that each and every person must adhere to His commandments to receive the saving ordinances, He is also merciful, loving, and kind, not condemning those who never had an opportunity to do so before their sojourn on earth was through.&lt;/p&gt;

&lt;p&gt;Of this, &lt;a href=&quot;https://www.churchofjesuschrist.org/manual/teachings-joseph-smith/chapter-41&quot;&gt;Joseph aptly said&lt;/a&gt;, “This doctrine appears glorious, inasmuch as it exhibits the greatness of divine compassion and benevolence in the extent of the plan of human salvation…This glorious truth is well calculated to enlarge the understanding, and to sustain the soul under troubles, difficulties and distresses.”&lt;/p&gt;

&lt;p&gt;Brothers and sisters, I know that Heavenly Father loves us. Not only because of this beautiful doctrine, but because He has revealed it to us so that we may have peace and hope concerning our brothers and sisters who leave this life without a knowledge of Him.&lt;/p&gt;

&lt;p&gt;One of the most amazing parts of this doctrine is that we have the power to help our ancestors in this way. By performing this work in the temple we are becoming “saviors on Mount Zion.” Note that the &lt;em&gt;s&lt;/em&gt; in &lt;em&gt;saviors&lt;/em&gt; is lowercase, for there is only one Savior; however, we are indeed performing a necessary work for our ancestors that they can no longer perform for themselves.&lt;/p&gt;

&lt;p&gt;As Voltaire (and later &lt;a href=&quot;http://en.wikipedia.org/wiki/Uncle_Ben#.22With_great_power_comes_great_responsibility.22&quot;&gt;a wise comic book character&lt;/a&gt;) once said, “With great power comes great responsibility.” It is up to us to perform this ever-important work. Joseph warned: “Those Saints who neglect it in behalf of their deceased relatives, do it at the peril of their own salvation.” This helps us realize that performing vicariously these saving ordinances is not just for our ancestors’ benefit, but also for our own.&lt;/p&gt;

&lt;p&gt;Elder David A. Bednar, in an &lt;a href=&quot;https://www.churchofjesuschrist.org/general-conference/2011/10/the-hearts-of-the-children-shall-turn&quot;&gt;October 2011 General Conference address&lt;/a&gt;, promised the following:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;As you respond in faith to this invitation, your hearts shall turn to the fathers. The promises made to Abraham, Isaac, and Jacob will be implanted in your hearts. Your patriarchal blessing, with its declaration of lineage, will link you to these fathers and be more meaningful to you. Your love and gratitude for your ancestors will increase. Your testimony of and conversion to the Savior will become deep and abiding. And I promise you will be protected against the intensifying influence of the adversary. As you participate in and love this holy work, you will be safeguarded…throughout your lives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The benefits of family history work are not only spiritual. The following is an excerpt from an article in the New York Times by Bruce Feiler entitled &lt;a href=&quot;http://www.nytimes.com/2013/03/17/fashion/the-family-stories-that-bind-us-this-life.html&quot;&gt;“The Family Stories that Bind Us,”&lt;/a&gt; published March 15th, 2013:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;…my dad called me to his bedside. There was a palpable sense of fear I couldn’t remember hearing before.&lt;/p&gt;

  &lt;p&gt;“Our family’s falling apart,” he said.&lt;/p&gt;

  &lt;p&gt;“No it’s not,” I said instinctively. “It’s stronger than ever.”&lt;/p&gt;

  &lt;p&gt;But lying in bed afterward, I began to wonder: Was he right? What is the secret sauce that holds a family together? What are the ingredients that make some families effective, resilient, happy?&lt;/p&gt;

  &lt;p&gt;Myth-shattering research has reshaped our understanding of dinnertime, discipline and difficult conversations. … [In this research] a surprising theme [has] emerged. The single most important thing you can do for your family may be the simplest of all: develop a strong family narrative.&lt;/p&gt;

  &lt;p&gt;I first heard this idea from Marshall Duke, a colorful psychologist at Emory University. In the mid-1990s, Dr. Duke was asked to help explore myth and ritual in American families.&lt;/p&gt;

  &lt;p&gt;“There was a lot of research at the time into the dissipation of the family,” he told me at his home in suburban Atlanta. “But we were more interested in what families could do to counteract those forces.”&lt;/p&gt;

  &lt;p&gt;Around that time, Dr. Duke’s wife, Sara, a psychologist who works with children with learning disabilities, noticed something about her students.&lt;/p&gt;

  &lt;p&gt;“The ones who know a lot about their families tend to do better when they face challenges,” she said.&lt;/p&gt;

  &lt;p&gt;Her husband was intrigued, and along with a colleague, Robyn Fivush, set out to test her hypothesis. They developed a measure called the “Do You Know?” scale that asked children to answer 20 questions.&lt;/p&gt;

  &lt;p&gt;Examples included: Do you know where your grandparents grew up? Do you know where your mom and dad went to high school? Do you know where your parents met? Do you know an illness or something really terrible that happened in your family? Do you know the story of your birth?&lt;/p&gt;

  &lt;p&gt;Dr. Duke and Dr. Fivush asked those questions of four dozen families in the summer of 2001, and taped several of their dinner table conversations. They then compared the children’s results to a battery of psychological tests the children had taken, and reached an overwhelming conclusion. The more children knew about their family’s history, the stronger their sense of control over their lives, the higher their self-esteem and the more successfully they believed their families functioned. The “Do You Know?” scale turned out to be the best single predictor of children’s emotional health and happiness.&lt;/p&gt;

  &lt;p&gt;…Dr. Duke said that children who have the most self-confidence have what he and Dr. Fivush call a strong “intergenerational self.” They know they belong to something bigger than themselves.&lt;/p&gt;

  &lt;p&gt;…The bottom line: if you want a happier family, create, refine and retell the story of your family’s positive moments and your ability to bounce back from the difficult ones. That act alone may increase the odds that your family will thrive for many generations to come.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I believe that there is eternal truth at the heart of these doctors’ research that can be summarized by &lt;a href=&quot;https://www.churchofjesuschrist.org/scriptures/ot/mal/4&quot;&gt;Malachi chapter four, verse six&lt;/a&gt;, which says: “And he shall turn the heart of the fathers to the children, and the heart of the children to their fathers, lest I come and smite the earth with a curse.”&lt;/p&gt;

&lt;p&gt;Perhaps the greatest blessing, though, is the joy we feel when we participate in the Lord’s work. &lt;a href=&quot;https://www.churchofjesuschrist.org/liahona/2014/06/hastening-the-work&quot;&gt;President Joseph F. Smith once said&lt;/a&gt;, “Through our efforts in their behalf their chains of bondage will fall from them, and the darkness surrounding them will clear away, that light may shine upon them and they shall hear in the spirit world of the work that has been done for them by their children here, and will rejoice with you in their performance of these duties.”&lt;/p&gt;

&lt;p&gt;We taste of the joy that President Joseph F. Smith is talking about when we do missionary work. In fact, family history work can be considered missionary work for the dead. Both involve bringing the gospel to those who did not have it before. Both involve some sort of sacrifice on our part. Both involve faith.&lt;/p&gt;

&lt;p&gt;This month’s special message from President Monson (the current prophet and president of the Church) is entitled &lt;a href=&quot;https://www.churchofjesuschrist.org/liahona/2014/06/hastening-the-work&quot;&gt;“Hastening the Work.”&lt;/a&gt; When I think of hastening work, I think about working extra hard at the end of the day to make sure that everything is done; I think about expending extra effort in the ninth inning of a close baseball game between rival teams; I think about scrambling to finish an exam before the exam center closes. The Lord has said through His prophets that He is hastening His work in &lt;em&gt;these times&lt;/em&gt;. This is not limited to full-time missionary service. President Monson reminds us that “The Lord has never…indicated that His work is confined to mortality. Rather, His work embraces eternity.” Temple ordinances for the dead, therefore, are a vital part of this work.&lt;/p&gt;

&lt;p&gt;If you’ve never done family history work before, that’s OK. Neither have I. Now is a great time to start. &lt;a href=&quot;https://www.churchofjesuschrist.org/liahona/2014/06/hastening-the-work&quot;&gt;President Monson said&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We are going to make mistakes, but none of us can become an expert in family history work without first being a novice. Therefore, we must plunge into this work, and we must prepare for some uphill climbing. This is not an easy task, but the Lord has placed it upon you, and He has placed it upon me.&lt;/p&gt;

  &lt;p&gt;As you pursue family history work, you are going to find yourself running into roadblocks, and you are going to say to yourself, “There is nothing else I can do.” When you come to that point, get down on your knees and ask the Lord to open the way, and He will open the way for you. I testify that this is true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Lord has already provided us with many valuable resources to aid in His work. The Church’s familysearch.org website is a wonderful place to start. Within minutes after logging in, I was reading stories written by my ancestors, seeing their pictures, and realizing that they are real people who had lives and experiences just like we do. This morning I happened upon an experience written by my great-grandfather, George Samuel Romney, that recounted one of his brother’s journeys through the Mexican wilderness. This brother, Miles Archibald Romney, befriended a dog who would not stop following him. As he camped for the night and slept, he awoke to the growling of the dog and discovered that there was a thief approaching him with “a huge dagger with which he expected to strike the deadly blow.” Miles quickly readied his rifle and warned the intruder that he would defend himself if necessary. Frightened, the thief fled. The story concludes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Miles, when he was left alone with the dog he gave him a fond caress while tears of gratitude glistened in his eyes there in the moonlight. The attempt of the bandit had been frustrated and a life had been saved by the growl of a dog. … [Upon resuming his journey in the morning] he cast a backward glance [and] to his astonishment the dog could not be seen.  For several miles he followed back the trail but his faithful friend could not be found.  As mysteriously as the animal had come into the life of the traveler did it pass out again.  Surely, thought my brother as he pushed on toward home there is an unseen power protecting the lives of men from dangers of which they are unaware…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My faith was strengthened by reading this story. And thanks to modern technology, with a few clicks of the mouse I was able to see that all the temple work for Miles had been completed a few years ago.&lt;/p&gt;

&lt;p&gt;Our ancestors need our help. &lt;a href=&quot;https://www.churchofjesuschrist.org/liahona/2014/06/hastening-the-work&quot;&gt;President Monson said&lt;/a&gt;, “We, as spirit children of our Heavenly Father, were sent to earth at this time that we might participate in hastening this great work… Our job is to search out our dead and then go to the temple and perform the sacred ordinances that will bring those beyond the veil the same opportunities we have.”&lt;/p&gt;

&lt;p&gt;If you’d like to learn more about family history, I encourage you to attend the Family History Sunday School class that starts next week.&lt;/p&gt;

&lt;p&gt;Brothers and sisters, though I am very new to family history work, I can already testify of the additional blessings we will receive and will allow our ancestors to receive if we participate. I know that the plan of salvation is a selfless plan of love. I know that Jesus Christ is our Savior. In his name, the name of Jesus Christ, amen.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 08 Jun 2014 17:50:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/how-the-gospel-of-christ-relates-to-those-who-passed-on-without-it/</guid>
    </item>
    
    <item>
      <title>What Really Matters</title>
      <link>https://mjswensen.com/blog/what-really-matters/</link>
      <description>When life's challenges affront us, the superfluity of daily life suddenly loses its meaning.</description>
      <content:encoded>&lt;p&gt;When life’s challenges affront us, the superfluity of daily life suddenly loses its meaning.&lt;/p&gt;

&lt;p&gt;My wife and I are the parents of one child, a nearly two-year-old boy. He is our first child, and I am therefore a little clueless when it comes to parenting (my wife is much further along than me in that regard). This week he managed to come down with what was later found to be a gastrointestinal bug of some sort, with an ear infection and cold on top of that. When he woke up with a 105° fever, we rushed him to the pediatrician’s office. He was dehydrated, and was admitted to the hospital that night. Since this was our first bout with any health-related issues for our son, one can hopefully understand that we felt anxious and worried. Fortunately, after taking in some fluids intravenously he began to recover without a problem. The point of the story is not the ending—which, thankfully, was a happy one—but what happened during the difficult experience.&lt;/p&gt;

&lt;p&gt;When life gets challenging, I’m convinced that we need at least three things to get us through:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Family/close friends&lt;/li&gt;
  &lt;li&gt;A close-knit community&lt;/li&gt;
  &lt;li&gt;Hope that things will turn out&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;familyclose-friends&quot;&gt;Family/Close Friends&lt;/h2&gt;

&lt;p&gt;We were overwhelmed by the amount of love and support we felt when our family and friends caught wind that we were in the hospital with our son. Upon arriving home, we found that a group of our close friends had cleaned and organized our entire apartment! My sister showed up unannounced a few hours later with dinner. Some other friends, knowing that we would be surely taken care of, decided to leave a half-dozen of my favorite Krispy Kreme donuts with a note to let us know they were thinking about us. You can imagine how we felt!&lt;/p&gt;

&lt;h2 id=&quot;a-close-knit-community&quot;&gt;A Close-knit Community&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://churchofjesuschrist.org&quot;&gt;Church of Jesus Christ&lt;/a&gt; serves my family well in this regard. Leaders of our local congregation organized volunteers to bring us dinner for three nights following our return home from the hospital. When we attended our church meetings that Sunday, our friends at church showed genuine concern for our well-being and were relieved to hear that everything was OK. Since it is a global church, we know that no matter where we may relocate to in the future there will be a warm community there to welcome us.&lt;/p&gt;

&lt;h2 id=&quot;hope&quot;&gt;Hope&lt;/h2&gt;

&lt;p&gt;This is perhaps the most important factor to our ability to handle life’s curve balls. For me, hope comes from &lt;a href=&quot;https://www.comeuntochrist.org/faq/topic/family/question/mormon-families&quot;&gt;my belief that I will be able to be with my family in the afterlife&lt;/a&gt;. I knew that no matter what happened to my precious baby son, he is my son both in life and after death. This is possible through Jesus Christ, and brings me great peace each day. The following Easter-themed video shows why we can have hope in Jesus Christ:&lt;/p&gt;

&lt;iframe src=&quot;//www.youtube.com/embed/_S3TI4bYerU&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;I encourage any seeker of peace and comfort to be sure that these three anchors are intact before pursuing any other endeavor.&lt;/p&gt;
</content:encoded>
      <pubDate>Thu, 24 Apr 2014 14:29:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/what-really-matters/</guid>
    </item>
    
    <item>
      <title>Lucidpress: Publishing for Web and Print</title>
      <link>https://mjswensen.com/blog/lucidpress/</link>
      <description>Lucidpress is a new cloud-based-yet-desktop-caliber publishing application.</description>
      <content:encoded>&lt;p&gt;&lt;a href=&quot;http://www.lucidpress.com&quot;&gt;Lucidpress&lt;/a&gt; is a new cloud-based-yet-desktop-caliber publishing application. If you’re a designer or involved with a small- or medium-sized business, you should probably check it out—it’s free during beta. There’s also an interactive &lt;a href=&quot;http://www.lucidpress.com/demo&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a short introduction:&lt;/p&gt;

&lt;iframe src=&quot;//www.youtube.com/embed/NNhCSEVVd-o&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Check out what others are saying about Lucidpress:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;TechCrunch — &lt;a href=&quot;http://techcrunch.com/2013/10/02/lucidpress-launches-web-based-design-and-publishing-app-for-print-and-online/&quot;&gt;Lucidpress Launches Web-Based Design And Publishing App For Print And Online&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The Next Web — &lt;a href=&quot;http://thenextweb.com/insider/2013/10/02/lucid-software-brings-the-power-of-desktop-publishing-to-the-web-with-lucidpress-now-in-beta/&quot;&gt;Lucid Software brings the power of desktop publishing to the Web with Lucidpress, now in beta&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;PC World — &lt;a href=&quot;http://www.pcworld.com/article/2051340/lucidpress-drop-and-drag-publishing-app-simplifies-document-design.html&quot;&gt;Lucidpress: drop-and-drag publishing app simplifies document design&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;AppStorm Web — &lt;a href=&quot;http://web.appstorm.net/reviews/office/lucidpress-brings-advanced-page-layout-tools-to-the-browser&quot;&gt;Lucidpress Brings Advanced Page Layout Tools to the Browser&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;VentureBeat — &lt;a href=&quot;http://venturebeat.com/2013/10/02/underdog-lucid-adobe-microsoft-free-design-app/&quot;&gt;Underdog Lucid takes on Adobe, Microsoft with free design app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;About.com — &lt;a href=&quot;http://collaboration.about.com/od/groupproductivity/fl/Lucidpress-Slick-Web-based-Design-App-for-Novice-Users.htm&quot;&gt;Lucidpress Slick Web-based Design App for Novice Users&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;PC World — &lt;a href=&quot;http://www.pcworld.com/article/2066982/lucidpress-review-free-beta-makes-typesetting-simple-accessible-and-fun.html&quot;&gt;Lucidpress review: Free beta makes typesetting simple, accessible, and fun&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;The Red Ferret — &lt;a href=&quot;http://www.redferret.net/?p=39919&quot;&gt;Lucidpress – superb new free collaborative web based layout and design application does it all&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;AddictiveTips — &lt;a href=&quot;http://www.addictivetips.com/web/lucidpress-free-collaborative-web-based-adobe-indesign-alternative/&quot;&gt;Lucidpress Is A Collaborative Web-Based Alternative To Adobe InDesign&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;FreeTech4Teachers.com — &lt;a href=&quot;http://www.freetech4teachers.com/2013/10/collaboratively-create-multimedia.html&quot;&gt;Collaboratively Create Multimedia Documents With Lucidpress&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;MarketingTechBlog.com — &lt;a href=&quot;http://www.marketingtechblog.com/lucidpress/&quot;&gt;Lucidpress: Collaborative Online Print &amp;amp; Digital Publishing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;EducatorsTechnology.com — &lt;a href=&quot;http://www.educatorstechnology.com/2013/10/lucidpress-good-real-time-collaborative.html&quot;&gt;Lucidpress: A Good Real-time Collaborative Tool for Teachers and Students&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;CreativePro.com — &lt;a href=&quot;http://www.creativepro.com/article/design-for-print-and-digital-publishing-with-lucidpress&quot;&gt;Design for Print and Digital Publishing With Lucidpress&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;PubExec.com — &lt;a href=&quot;http://www.pubexec.com/article/drag-and-drop-software-makes-publishing-accessible/1&quot;&gt;Drag-and-Drop Software Makes Publishing Accessible&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;MakeUseOf.com — &lt;a href=&quot;http://www.makeuseof.com/tag/lucidpress-free-online-indesign-alternative-creative-needs/&quot;&gt;Lucidpress: The Free Online InDesign Alternative For Your Creative Needs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, what are you waiting for? Go &lt;a href=&quot;http://www.lucidpress.com/demo&quot;&gt;check it out!&lt;/a&gt;&lt;/p&gt;
</content:encoded>
      <pubDate>Mon, 30 Dec 2013 21:21:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/lucidpress/</guid>
    </item>
    
    <item>
      <title>Basic Beliefs of the Church of Jesus Christ</title>
      <link>https://mjswensen.com/blog/basic-beliefs-of-the-church-of-jesus-christ/</link>
      <description>The story behind the Church of Jesus Christ is remarkably simple and powerful.</description>
      <content:encoded>&lt;p&gt;According to &lt;a href=&quot;https://comeuntochrist.org&quot;&gt;one of the Church’s official websites&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Church of Jesus Christ of Latter-day Saints is the official name of the religion commonly called the Mormon Church. We believe first and foremost that Jesus Christ is the Savior of the world and the Son of God.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The story behind the Church is remarkably simple and powerful. Be sure to check out &lt;a href=&quot;https://comeuntochrist.org/beliefs/restoration&quot;&gt;a brief history of the Church&lt;/a&gt; and &lt;a href=&quot;https://comeuntochrist.org/beliefs/jesus-christ&quot;&gt;a succinct description of the Church’s core doctrines&lt;/a&gt;. Good stuff.&lt;/p&gt;
</content:encoded>
      <pubDate>Sun, 03 Nov 2013 15:13:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/basic-beliefs-of-the-church-of-jesus-christ/</guid>
    </item>
    
    <item>
      <title>CSS-only Text Gradients</title>
      <link>https://mjswensen.com/blog/css-only-text-gradients/</link>
      <description>The things you can do with CSS these days are amazing. I was inspired by the beautiful Jekyll logo and set out to see if I could do something similar using only CSS.</description>
      <content:encoded>&lt;p&gt;The things you can do with CSS these days are amazing. I was inspired by the beautiful &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; logo and set out to see if I could do something similar using only CSS.&lt;/p&gt;

&lt;p&gt;This is the look I was going for:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/jekyll-logo.png&quot; alt=&quot;Jekyll Logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I was excited to find on my first Google attempt &lt;a href=&quot;http://css-tricks.com/snippets/css/gradient-text/&quot;&gt;a succinct post&lt;/a&gt; by the awesome &lt;a href=&quot;http://chriscoyier.net/&quot;&gt;Chris Coyier&lt;/a&gt; and I had achieved the desired effect in minutes. I thought it would be nice for a new project I’m working on called &lt;a href=&quot;https://github.com/mjswensen/scribbli&quot;&gt;Scribbli&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the complete Sass code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;53%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fallback&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;-webkit-linear-gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lighten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-text-fill-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And this is the end result for Webkit browsers (Chrome, Safari, Opera):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/scribbli-logo-final.png&quot; alt=&quot;End Result&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;breakdown&quot;&gt;Breakdown&lt;/h2&gt;

&lt;p&gt;I thought we could take a closer look at what is going on here. First, let’s take a look at the fallback for non-Webkit browsers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;53%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fallback&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This gives us a simple text color with no effect. Not as cool as the detail we get with the gradient and the shadow, but good enough for a fallback.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/scribbli-logo-fallback.png&quot; alt=&quot;Fallback&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The trick is in this next part. We set a background gradient on the entire &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;h1&lt;/code&gt; element, but we restrict the visibility of the gradient to the form of the text! We also need to make the text transparent so that the gradient isn’t hidden by the flat yellow fallback we set earlier.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;53%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fallback&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;-webkit-linear-gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lighten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-text-fill-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that we can use some nifty Sass functions to calculate the color stops for the gradient with minimal effort. Also note that for the background gradient, we are only using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-webkit-&lt;/code&gt; prefix and omitting the prefixes for the other browsers. This is because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-fill-color&lt;/code&gt; is only currently supported in Webkit and we don’t want the background gradient there if the rest of it doesn’t work.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/scribbli-logo-clipped-gradient.png&quot; alt=&quot;Clipped Gradient Background&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Cool. We’re almost there. Last thing to do is to add a little shadow to give the text some depth. Adding a text shadow to transparent text is different than the usual text shadow effect, because you can see the entire shadow under the text. For example: this is what the shadow looks like if we remove the background clipping mask:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/scribbli-logo-shadow.png&quot; alt=&quot;Transparent Text Shadow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Anyway, putting it all together again:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sass&quot; data-lang=&quot;sass&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;53%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Fallback&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;-webkit-linear-gradient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;lighten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;text&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;-webkit-text-fill-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hsla&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Even though the shadow drastically affects the apparent colors of the text since it is fully visible, it actually gives a nice embossed effect with the slight vertical offset. Once again, here is a screenshot of the final result; fully selectable, SEO-friendly text!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/scribbli-logo-final.png&quot; alt=&quot;End Result&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://css-tricks.com/image-under-text/&quot;&gt;Here&lt;/a&gt; is related and more in-depth article from Chris Coyier.&lt;/p&gt;
</content:encoded>
      <pubDate>Tue, 08 Oct 2013 01:52:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/css-only-text-gradients/</guid>
    </item>
    
    <item>
      <title>The Psychology Behind Open Source</title>
      <link>https://mjswensen.com/blog/the-psychology-behind-open-source/</link>
      <description>I've always wondered what motivates people (including myself) to contribute to the open source community. Dan Pink hits the nail on the head and the RSA animation drives the point home.</description>
      <content:encoded>&lt;p&gt;This video is seriously cool. I’ve always wondered what motivates people (including myself) to contribute to the open source community. &lt;a href=&quot;http://www.danpink.com&quot;&gt;Dan Pink&lt;/a&gt; hits the nail on the head and the &lt;a href=&quot;http://www.thersa.org&quot;&gt;RSA&lt;/a&gt; animation drives the point home.&lt;/p&gt;

&lt;iframe src=&quot;//www.youtube.com/embed/u6XAPnuFjJc&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

</content:encoded>
      <pubDate>Thu, 03 Oct 2013 15:30:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/the-psychology-behind-open-source/</guid>
    </item>
    
    <item>
      <title>Git</title>
      <link>https://mjswensen.com/blog/git/</link>
      <description>This post is intended for the budding programmer who is ready to take their productivity to the next level. Git is one of the best ways to do just that.</description>
      <content:encoded>&lt;p&gt;What better topic for my first post than my favorite piece of software? This post is intended for the budding programmer who is ready to take their productivity to the next level. Git is one of the best ways to do just that.&lt;/p&gt;

&lt;p&gt;Git has many features and a comprehensive review of any one of them is far beyond the scope of this post. Instead, we will cover the basic idea behind Git and why you should use it if you don’t already.&lt;/p&gt;

&lt;h2 id=&quot;what-is-git&quot;&gt;What is Git?&lt;/h2&gt;

&lt;p&gt;Git is a distributed source code version control system. It keeps track of the revision (change) history for your code, recording snapshots of your codebase’s history while you work. It is also a powerful tool for collaborating with other developers on a project. If you’ve never used Git before, you might think that you don’t need it—we’ll get to that in the next section.&lt;/p&gt;

&lt;h2 id=&quot;why-use-git&quot;&gt;Why use Git?&lt;/h2&gt;

&lt;p&gt;These are my top reasons to use Git:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Increased productivity&lt;/li&gt;
  &lt;li&gt;Better collaboration&lt;/li&gt;
  &lt;li&gt;Automatic redundancy&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;1-increased-productivity&quot;&gt;1. Increased Productivity&lt;/h3&gt;

&lt;p&gt;Have you ever been writing a program and realized that you broke something that worked previously, but have no idea how many changes you have made since it stopped working? You then begin a frantic series of undos in hopes to get your program back to a working state. (And if you’re lucky enough to find one, you have to redevelop the progress you just undid.)&lt;/p&gt;

&lt;p&gt;There’s a better way.&lt;/p&gt;

&lt;p&gt;With Git, you periodically &lt;em&gt;commit&lt;/em&gt; your program as you develop. That way, when you’ve made some progress, you can commit your code and then move forward knowing that if something goes wrong you can always safely revert to a previous state. You can even delete files and Git will re-add them if you tell it to.&lt;/p&gt;

&lt;h3 id=&quot;2-better-collaboration&quot;&gt;2. Better Collaboration&lt;/h3&gt;

&lt;p&gt;Git is the go-to standard for collaborative coding. Often there will be a designated server to host a Git repository that developers of a team will &lt;em&gt;pull&lt;/em&gt; from and &lt;em&gt;push&lt;/em&gt; to. Git makes it easy to develop different features simultaneously and even merge conflicts where necessary. One of the most popular hosted Git solutions is &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;, which has a special focus on highly collaborative, open-source software.&lt;/p&gt;

&lt;h3 id=&quot;3-automatic-redundancy&quot;&gt;3. Automatic Redundancy&lt;/h3&gt;

&lt;p&gt;One significant advantage of Git is that if your team’s central Git server were to go down, each computer that cloned the repository has a full backup of the code including all of its history. Team members could then push and pull code from each other until the issue with the server could be resolved. This is not possible with some other version control systems.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Git is a powerful tool to add to your toolkit. Learning it is an investment that is sure to pay off.&lt;/p&gt;

&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h2&gt;

&lt;p&gt;There is a great (free!) book about Git called &lt;a href=&quot;https://git-scm.com/book&quot;&gt;&lt;em&gt;Pro Git&lt;/em&gt;&lt;/a&gt; which gives a thorough explanation of Git usage for all experience levels. It’s available in various formats and languages. I would recommend going there next!&lt;/p&gt;
</content:encoded>
      <pubDate>Mon, 26 Aug 2013 14:10:00 +0000</pubDate>
      <guid>https://mjswensen.com/blog/git/</guid>
    </item>
    
  </channel>
</rss>
