<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryan Boone</title>
    <description>The latest articles on DEV Community by Ryan Boone (@falldowngoboone).</description>
    <link>https://dev.to/falldowngoboone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F295770%2Fa76b127d-1218-4a5c-b6d5-f03be7143e18.png</url>
      <title>DEV Community: Ryan Boone</title>
      <link>https://dev.to/falldowngoboone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/falldowngoboone"/>
    <language>en</language>
    <item>
      <title>Misadventures in web components</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Mon, 17 May 2021 00:18:06 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/misadventures-in-web-components-2j5</link>
      <guid>https://dev.to/falldowngoboone/misadventures-in-web-components-2j5</guid>
      <description>&lt;p&gt;I have been thinking about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components"&gt;web components&lt;/a&gt; a lot lately, and I wanted to see how we could start using them at &lt;a href="https://www.containerstore.com/welcome.htm"&gt;The Container Store&lt;/a&gt;. The idea was to pick a simple component and recreate it as a web component, and the first candidate that came to mind is our frequently-used quantity stepper. The stepper appears in several places throughout the website, and it's dependent on an embarrassing amount of jQuery.&lt;/p&gt;

&lt;p&gt;Here's my humble first attempt:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/falldowngoboone/embed/xxgMEax?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The result isn't perfect, but I gained a better understanding of web components, their limitations, and where they're useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;My ultimate goal was to create a web component that progressively enhanced from a run-of-the-mill number input to a custom element. I also wanted to explore the limitations of web components inside a form. What I ended up with was this weird solution that sidesteps the shadow DOM altogether.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progressive enhancement...sort of
&lt;/h3&gt;

&lt;p&gt;The experimental component requires a donor number &lt;code&gt;input&lt;/code&gt;, either as a child of &lt;code&gt;my-stepper&lt;/code&gt; or, my preference, via a &lt;code&gt;data-is&lt;/code&gt; attribute on a native &lt;code&gt;input[type=number]&lt;/code&gt;. This is my naive version of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#customized_built-in_elements"&gt;customized built-ins&lt;/a&gt;, which I suspect would be perfect for this particular situation.&lt;/p&gt;

&lt;p&gt;The reason for the hand-rolled functionality is because &lt;a href="https://github.com/WICG/webcomponents/issues/509#issuecomment-230700060"&gt;Safari doesn't support customized built-ins&lt;/a&gt;, nor do they intend to anytime soon&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. I will probably swap out my custom &lt;code&gt;data&lt;/code&gt; attribute solution for a &lt;a href="https://github.com/ungap/custom-elements"&gt;polyfill that supports the native &lt;code&gt;is&lt;/code&gt; attribute&lt;/a&gt; when implementing in production because this is not implemented to spec.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;my-stepper&lt;/code&gt;'s template inserts child content in between two &lt;code&gt;button&lt;/code&gt;s wired up with click listeners. The listeners increment or decrement the value of the &lt;code&gt;input&lt;/code&gt; (if present). The interesting thing about template &lt;code&gt;slot&lt;/code&gt;s is their content remains in the light DOM, making them completely accessible to the parent form.&lt;/p&gt;

&lt;p&gt;The result ensures that the input remains an input if JavaScript is disabled or (more than likely) takes a while to load, parse and execute&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Native form functionality
&lt;/h3&gt;

&lt;p&gt;The shadow DOM encapsulates style and markup, but that comes at the cost of accessing field data in form field web components. Shadow DOM field values aren't registered in &lt;code&gt;form.elements&lt;/code&gt; and shadow fields cannot participate in the form lifecycle (e.g. field validation) by default.&lt;/p&gt;

&lt;p&gt;If you need to access field values, you can &lt;a href="https://www.hjorthhansen.dev/shadow-dom-form-participation/"&gt;use a hidden input&lt;/a&gt; or &lt;a href="https://web.dev/more-capable-form-controls/"&gt;listen for the &lt;code&gt;formdata&lt;/code&gt; event on the parent form&lt;/a&gt;. Both strategies ensure you can pass data properly on submit, but neither will give you full access to the form lifecycle.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://html.spec.whatwg.org/multipage/custom-elements.html#element-internals"&gt;&lt;code&gt;ElementInternals&lt;/code&gt;&lt;/a&gt; interface, however, officially grants web components access to the lifecycle of a parent form, including methods to determine the field's value and validity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachInternals&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// use internals to set the form field value, &lt;/span&gt;
    &lt;span class="c1"&gt;// determine valid data, etc.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to gaining access to the form lifecycle, the &lt;code&gt;ElementInternals&lt;/code&gt; specification grants access to the &lt;a href="https://html.spec.whatwg.org/multipage/custom-elements.html#accessibility-semantics"&gt;accessibility object model&lt;/a&gt;. &lt;a href="https://caniuse.com/?search=attachinternals"&gt;Only Chrome and Chromium-based browsers&lt;/a&gt; support internals at the time of this writing, but, again, there are polyfills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;I learned that elements within the shadow DOM will still receive focus and are properly announced via a screen reader out-of-the-box (curiously, VoiceOver announces shadow DOM barriers as a new frame, at least at the time of this writing). I guess it acts sort of like an &lt;code&gt;iframe&lt;/code&gt; in that respect?&lt;/p&gt;

&lt;p&gt;One concern I had, though, was how to reference an ID in the shadow DOM with a &lt;code&gt;label&lt;/code&gt; in the light DOM. Unfortunately, shadow DOM ID reference is not possible, at least not natively. There have been discussions about somehow &lt;a href="https://github.com/whatwg/html/issues/3219"&gt;delegating labels via an option passed to &lt;code&gt;attachShadow&lt;/code&gt;&lt;/a&gt;, but I haven't seen anything regarding implementation.&lt;/p&gt;

&lt;p&gt;The only thing I found that works with the shadow DOM is determining the input's label(s)&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, then adding click listeners to each that imperatively focus the shadow DOM target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
&amp;lt;input name="name" /&amp;gt;
`&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;MyInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;formAssociated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachShadow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachInternals&lt;/span&gt;&lt;span class="p"&gt;?.()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hey, check it out, we're exercising the &lt;code&gt;ElementInternals&lt;/code&gt; API! That didn't take long.&lt;/p&gt;

&lt;p&gt;Note that we have to first specify that an element is form-associated with the &lt;code&gt;formAssociated&lt;/code&gt; static property, then we can access the form-related internals. Also, note that we have to attach the click listeners in the &lt;code&gt;connectedCallback&lt;/code&gt; method instead of the constructor (which is what I attempted at first)&lt;sup id="fnref4"&gt;4&lt;/sup&gt;. Form association only happens after the element has attached to the DOM, so &lt;code&gt;this.internals.labels&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; in the constructor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling
&lt;/h3&gt;

&lt;p&gt;There are &lt;a href="https://css-tricks.com/styling-web-components/"&gt;several ways to customize web component styles&lt;/a&gt;. For this experiment, I'm opening up custom styling via shadow parts and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part"&gt;&lt;code&gt;::part()&lt;/code&gt; CSS pseudo-element&lt;/a&gt;. I think this strategy works for this particular instance since there are only three pieces that need to be styled: the two buttons and the wrapper. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;::part()&lt;/code&gt; pseudo-element takes an identifier that is assigned with the &lt;code&gt;part&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;part=&lt;/span&gt;&lt;span class="s"&gt;"decrement control"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;minus;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&lt;/span&gt;CHILD CONTENT&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;part=&lt;/span&gt;&lt;span class="s"&gt;"increment control"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;my-stepper&lt;/span&gt;&lt;span class="nd"&gt;::part&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;control&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* styles here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that you can pass multiple identifiers to &lt;code&gt;part&lt;/code&gt;. I'm using this feature to allow consumers to style both buttons with the &lt;code&gt;control&lt;/code&gt; shadow part and the individual buttons with their respective shadow parts.&lt;/p&gt;

&lt;p&gt;Shadow parts may not scale that well with more complex elements, and I haven't tested how they would work on child web components (web components nested in a web component shadow DOM).&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, I think the next step is to redo this component as a customized built-in and let a &lt;a href="https://github.com/ungap/custom-elements"&gt;polyfill&lt;/a&gt; or library do all the heavy lifting. I'm curious to see what role, if any, shadow DOM plays in that particular type of web component.&lt;/p&gt;

&lt;p&gt;I'm also interested in exploring &lt;a href="https://lit.dev"&gt;LitElement&lt;/a&gt; and &lt;a href="https://stenciljs.com"&gt;Stencil.js&lt;/a&gt;, particularly how they would integrate with our current stack. My ultimate goal is to make web component creation as easy and gotcha-free as possible, and libraries help normalize some of the weirdness you may get with a low-level API like web components.&lt;/p&gt;

&lt;p&gt;I had a ton of fun messing around with web components, and I learned a lot as well. If you found this helpful or have something you'd like me to write about, let me know. I enjoy doing these experiments and hope to deep dive even more into web components in the future.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More on web components and the accessibility object model: &lt;a href="https://www.24a11y.com/2019/web-components-and-the-aom/"&gt;https://www.24a11y.com/2019/web-components-and-the-aom/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Best practices: &lt;a href="https://developers.google.com/web/fundamentals/web-components/best-practices"&gt;https://developers.google.com/web/fundamentals/web-components/best-practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Follow the latest in web components: &lt;a href="https://www.webcomponents.org/"&gt;https://www.webcomponents.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A nice overview of web component criticism: &lt;a href="https://blog.logrocket.com/what-happened-to-web-components/"&gt;https://blog.logrocket.com/what-happened-to-web-components/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Safari engineers argue that customized built-ins violate the &lt;a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle"&gt;Liskov substitution principle&lt;/a&gt;. Given the fact that this custom stepper will only support an &lt;code&gt;input[type=number]&lt;/code&gt;, I believe they're probably right. But also, &lt;code&gt;HTMLInputeElement&lt;/code&gt; is the element API equivalent of a dumpster fire. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Yes, the site should just run faster, I agree. Baby steps. And in an ideal world, we would pre-render shadow DOM on the server and hydrate it client-side. Currently, shadow DOM is imperative-only, so there's no way to render on the server, but there is a proposal for declarative shadow DOM, something I hope to write about soon. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Remember, labelable elements can have more than one label. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;It's a good idea to always run your side effects like attaching listeners inside &lt;code&gt;connectedCallback&lt;/code&gt;, even if you have access in the constructor. And make sure you clean up any listeners in the &lt;code&gt;disconnectedCallback&lt;/code&gt; method. See the resources section for a link to web component best practices. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>progressiveenhancement</category>
    </item>
    <item>
      <title>Native form validation with JavaScript</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Sun, 28 Mar 2021 02:19:39 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/native-form-validation-with-javascript-1df4</link>
      <guid>https://dev.to/falldowngoboone/native-form-validation-with-javascript-1df4</guid>
      <description>&lt;p&gt;You don't need to install a validation library to create rich client-side form validation experiences. HTML5's native client-side form validation is widely-supported and easy to implement, and while its default functionality is limited, the native &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Constraint_validation" rel="noopener noreferrer"&gt;Constraint Validation API&lt;/a&gt; allows for custom behavior with JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the humble login form
&lt;/h2&gt;

&lt;p&gt;Let's build a login form. We'll use an input for email, password, and a submit button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- login.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"login-form"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/api/auth"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Log In&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a pretty bare-bones login form. There are two inputs, one with a type of &lt;code&gt;email&lt;/code&gt;, the other with a type of &lt;code&gt;password&lt;/code&gt;, and a submit button. The form posts to an endpoint that handles the login process on the server&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Even though we haven't yet done anything to the form, it already has some client-side validation built in, thanks to the &lt;code&gt;email&lt;/code&gt; input. If you were to enter an invalid value into the email field and submit, you would see something like this (screenshot from Chrome):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-1.png" alt="Chrome invalid email error, Please include an '@' in the email address"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser has blocked the submission of data, focused the first invalid field, and now we see an error message describing the issue. We have email validation, and at no point did we type &lt;code&gt;npm install&lt;/code&gt; and download half of all known JavaScript dependencies.&lt;/p&gt;

&lt;p&gt;There is a problem, though. If you submit with both fields left blank, the form submits as valid, but our backend script requires both fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requiring fields
&lt;/h2&gt;

&lt;p&gt;There's an easy fix for the valid blank fields. Let's add the &lt;code&gt;required&lt;/code&gt; attribute to both the email and password inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- login.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"login-form"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/api/auth"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&amp;lt;br/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Log In&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now submitting without an email or password will look something like this (again, screenshot from Chrome):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-2.png" alt="Chrome missing value error, Please fill out this field"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So far, so good. Who needs JavaScript?&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling error state
&lt;/h2&gt;

&lt;p&gt;You might want to style your error state to draw more attention to fields with issues. We can do that by using the &lt;code&gt;:invalid&lt;/code&gt; CSS pseudo-class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* login.css */&lt;/span&gt;

&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:invalid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;351&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;27%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we see the first issue: HTML forms run validation immediately. By default, both the email and password fields are blank, and since they are required, they immediately render invalid. Boo!&lt;/p&gt;

&lt;p&gt;Ideally, we would want the fields to appear valid until a user attempts to enter a value. We could choose either to validate on blur or wait until the user tries to submit the form. Either way, we want to validate invalidated fields as their values update to ensure the quickest feedback possible.&lt;/p&gt;

&lt;p&gt;Sadly, we have reached the limits of default HTML client-side form validation. But fear not! We have access in JavaScript to all that validation goodness in the form of the aforementioned Constraint Validation API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation, final form
&lt;/h2&gt;

&lt;p&gt;The Constraint Validation API gives you complete access to the built-in validation we've been using up to this point but with more control. The first rule of Constraint Validation, much like Fight Club, is to not talk about validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noValidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;noValidate&lt;/code&gt; turns off the native client-side validation, freeing us up to do whatever we want. Turning off validation with JavaScript ensures the default validation still runs if JavaScript never executes for whatever reason. It also prevents showing our invalid style preemptively.&lt;/p&gt;

&lt;p&gt;The first thing we should do is run validation when the form is submitted. To validate the entire form, use the form method &lt;code&gt;reportValidity&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;noValidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFormSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reportValidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// POST form data to backend with fetch&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;reportValidity&lt;/code&gt; runs the form validation, returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; based on whether or not the form is valid, and reports any validation errors to the user. We can use the return value to determine whether or not to post to the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marking invalid inputs
&lt;/h2&gt;

&lt;p&gt;Before taking the validation reins we could tie into the convenient &lt;code&gt;:invalid&lt;/code&gt; pseudo-class to style invalid fields, but now that's no longer an option. Let's fix that next.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;reportValidity&lt;/code&gt; runs, it will dispatch an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/invalid_event" rel="noopener noreferrer"&gt;&lt;code&gt;invalid&lt;/code&gt;&lt;/a&gt; event for each invalid field. This event is cancellable, but it doesn't bubble, which means we'll have to register an event handler on each element in the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleInvalidField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the &lt;code&gt;aria-invalid&lt;/code&gt; attribute to invalid elements, which not only communicates validity state to accessible technology, it gives us a new hook for our invalid styles&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* login.css */&lt;/span&gt;

&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:invalid&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;aria-invalid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;true&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;351&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;27%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever I can, I try to tie style to semantics. This helps communicate the importance of the selector's existence and avoids unnecessary styles that may not be that reusable outside of the current context.&lt;/p&gt;

&lt;p&gt;The styles work now, but they remain even after the input becomes valid. Before we can correct that, there's another problem we need to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our new validation pattern
&lt;/h2&gt;

&lt;p&gt;We need to talk briefly about how we want this custom validation to work. Luckily for us, form error UX is well-researched. For this example, I'm referring to &lt;a href="https://www.nngroup.com/articles/errors-forms-design-guidelines/" rel="noopener noreferrer"&gt;guidelines published by the Nielsen Norman Group&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here's a quick summary of the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each field will first validate on the &lt;code&gt;blur&lt;/code&gt; event. This will avoid displaying warnings too early.&lt;/li&gt;
&lt;li&gt;Once a field has been visited initially, it will validate on user input. Immediate feedback helps users verify the information they've entered is correct. This also addresses the invalid style issue we currently have.&lt;/li&gt;
&lt;li&gt;Errors will be displayed alongside the field. We'll replace the default error tooltips with inline messages that remain on screen as long as the field is invalid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First I'll add the validation on field blur. We'll add that to our previous field level JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// previous code&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleInvalidField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// new&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFieldBlur&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkValidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a field element dispatches a &lt;code&gt;blur&lt;/code&gt; event, we optimistically remove the &lt;code&gt;aria-invalid&lt;/code&gt; attribute and then run &lt;code&gt;checkValidity&lt;/code&gt;, which does everything &lt;code&gt;reportValidity&lt;/code&gt; does except report validation errors to the user. We'll handle error reporting ourselves later.&lt;/p&gt;

&lt;p&gt;Next, we need to run validation on user input, but only after a field has been previously visited. For that, we'll need some local state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validating on user input
&lt;/h2&gt;

&lt;p&gt;For tracking fields that have been visited, we'll use a simple JavaScript array. Once a field has been visited, we'll push it into the array. To check for visited status, we query the array to see if the field is included.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="c1"&gt;// new&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;visited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blur&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFieldBlur&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// new&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// REMOVED field.removeAttribute("aria-invalid");&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkValidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// new&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFieldInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have added a &lt;code&gt;visited&lt;/code&gt; array and are adding fields to it inside &lt;code&gt;handleFieldBlur&lt;/code&gt;. We're also removing the &lt;code&gt;aria-invalid&lt;/code&gt; attribute code since it's now handled in the new input handler.&lt;/p&gt;

&lt;p&gt;Inside the input handler, we prevent validation from running before the field has been visited with a short circuit. We check the field's validity using its &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ValidityState" rel="noopener noreferrer"&gt;&lt;code&gt;ValidityState&lt;/code&gt;&lt;/a&gt;, which is a handy object that contains everything related to field validation. More on that in a bit.&lt;/p&gt;

&lt;p&gt;At this point, the desired validation behavior is done. The final thing we need to do is add custom error styling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom inline errors
&lt;/h2&gt;

&lt;p&gt;The current error messages only show up on a submission attempt and have the default tooltip styling. We want to show the errors directly underneath the invalid fields and update them on blur and user input. We need to first create containers for the error messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- login.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"login-form"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/api/auth"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; 
          &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"email-error"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- new --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email-error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- new --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; 
          &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"password-error"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- new --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"password-error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- new --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Log In&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing to notice in the above markup is we've added an &lt;code&gt;aria-describedby&lt;/code&gt; attribute to both inputs. This attribute ensures that screen readers associate each error message with its respective input. When an invalid input is focused, the screen reader will announce the input's label and type, pause briefly, then announce the error&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;We now need to populate the error containers with the appropriate error messages. Luckily the input fields have access to their validation messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleInvalidField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// new&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// new, prevents default validation errors&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFieldInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// new&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// new&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// new&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorContainerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-describedby&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorContainerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we go any further, it looks like every time we set the error container text, we also set the &lt;code&gt;aria-invalid&lt;/code&gt; attribute on the field. Let's clean this logic up by moving it into a function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login-validate.js&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleInvalidField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setFieldValidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// function-ified&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleFieldInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setFieldValidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// here too&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="c1"&gt;// new&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setFieldValidity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;errorContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aria-invalid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what our custom errors look like now when we try to submit with blank inputs (I'm adding the red "X" with CSS):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.falldowngoboone.com%2Fblog%2Fnative-form-validation-with-javascript%2Fscreen-shot-3.png" alt="Custom form errors underneath email and password inputs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Better form validation
&lt;/h2&gt;

&lt;p&gt;At this point, there are some rough edges, but it's working more or less as we want it to. One annoying thing is in Chrome validation messages get wordy. You can substitute a custom validation message based on the type of validation error is associated with the field. I'll leave that as an exercise for you.&lt;/p&gt;

&lt;p&gt;This exploration only looked at required fields and email pattern validation, but there are a variety of validation criteria built into browser validation. Like the &lt;code&gt;email&lt;/code&gt; input type, there are several other &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Forms/HTML5_input_types" rel="noopener noreferrer"&gt;input types&lt;/a&gt; with built-in validation, as well as ways to limit the range or length of an input's value, and a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern" rel="noopener noreferrer"&gt;pattern attribute&lt;/a&gt; that gives you even more control over input validation for certain types of inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep exploring
&lt;/h2&gt;

&lt;p&gt;I hope this post has inspired you to explore the Constraint Validation API. Taking advantage of native APIs improves frontend performance and moves complex logic off of your plate. Use them whenever possible.&lt;/p&gt;

&lt;p&gt;Do you like Web APIs? What's your favorite? Which one mystifies you? Let me know on DEV Community or Twitter. And if you enjoyed this post, let me know by liking it on DEV Community and giving me a follow.&lt;/p&gt;

&lt;p&gt;Until next time, keep exploring!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Our imaginary backend script also validates and sanitizes the data posted from the form. Client-side validation is a user experience enhancement. Never trust user-generated data and always validate on the server. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;To learn more about ARIA attributes, roles, and relationships, check out the &lt;a href="https://www.w3.org/TR/2021/CR-wai-aria-1.2-20210302/" rel="noopener noreferrer"&gt;WAI-ARIA specification&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;code&gt;aria-describedby&lt;/code&gt; can take multiple IDs separated by spaces (&lt;code&gt;aria-described="first-id second-id third-id"&lt;/code&gt;). This is helpful if you have instructions associated with an input, like password constraints. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>forms</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Hold off on optimizing JavaScript performance</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Wed, 10 Mar 2021 23:07:14 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/hold-off-on-optimizing-javascript-performance-2f1p</link>
      <guid>https://dev.to/falldowngoboone/hold-off-on-optimizing-javascript-performance-2f1p</guid>
      <description>&lt;p&gt;As a developer, I love optimizing performance. Learning efficient algorithms makes me &lt;em&gt;feel&lt;/em&gt; like a software engineer. But performance is not the only developer concern, nor should it be the first or second. &lt;a href="https://twitter.com/DavidKPiano/status/1367855571077128196?s=20"&gt;David K. Piano recently pointed this out on Twitter&lt;/a&gt;, offering up a suggested framework:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make it work&lt;br&gt;
Make it always work&lt;br&gt;
Make it work for everyone&lt;br&gt;
Make it right&lt;br&gt;
Make it fast&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;David's suggestion expands on a well-known &lt;a href="https://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast"&gt;programming maxim attributed to Kent Beck&lt;/a&gt;, &lt;em&gt;Make it work, Make it right, Make it fast&lt;/em&gt;. What does that mean? I want to take some time to expand on each layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it work
&lt;/h2&gt;

&lt;p&gt;Making working code is the first concern. This step may seem obvious, but there are a few things to consider as you make the code work.&lt;/p&gt;

&lt;p&gt;First, consider a &lt;a href="https://www.agilealliance.org/glossary/tdd/#q=~(infinite~false~filters~(postType~(~'page~'post~'aa_book~'aa_event_session~'aa_experience_report~'aa_glossary~'aa_research_paper~'aa_video)~tags~(~'tdd))~searchTerm~'~sort~false~sortDirection~'asc~page~1)"&gt;test-driven development&lt;/a&gt; approach. At the very least, wrap your code in tests as you write. This will make the rest of the process faster and ensure you don't break core functionality.&lt;/p&gt;

&lt;p&gt;Second, don't worry about writing pretty code. If you find yourself debating what to name a variable, call it &lt;code&gt;thing1&lt;/code&gt; and move on. Use formatting tools like &lt;a href="https://prettier.io"&gt;Prettier&lt;/a&gt; to avoid thinking about white space and semicolons.&lt;/p&gt;

&lt;p&gt;Finally, try to go fast here. You only need working code. If you hit a roadblock, start over. &lt;a href="https://medium.com/swlh/coding-faster-make-it-work-then-make-it-good-6aa988ebd8ab"&gt;Writing sloppy code&lt;/a&gt; at the beginning can help you arrive faster at a better solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it always work
&lt;/h2&gt;

&lt;p&gt;Now we shift our attention to edge cases, cases involving less-common or unique data sets. These are the areas where bugs tend to show up. Here are some scenarios you might want to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No data&lt;/li&gt;
&lt;li&gt;A single piece of data&lt;/li&gt;
&lt;li&gt;The maximum amount of data&lt;/li&gt;
&lt;li&gt;One less than the maximum amount&lt;/li&gt;
&lt;li&gt;Bad (malformed) data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your code involves asynchronous calls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rejected promises&lt;/li&gt;
&lt;li&gt;A promise that never resolves&lt;/li&gt;
&lt;li&gt;Slow responses&lt;/li&gt;
&lt;li&gt;Immediate responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, take the time to write tests. Whether you follow test-driven development or write tests after the fact, they will be invaluable as you move up the layers of concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it work for everyone
&lt;/h2&gt;

&lt;p&gt;JavaScript is a funny language in that you can never be sure of the execution environment. Identify your feature's core functionality and make sure it works in a wide variety of browsers. Having a list of your code's official browser support helps in this step.&lt;/p&gt;

&lt;p&gt;There are two approaches to supporting multiple environments: &lt;a href="https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/"&gt;progressive enhancement&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation"&gt;graceful degradation&lt;/a&gt;. Both involve detecting feature support, but progressive enhancement adds functionality while graceful degradation removes functionality.&lt;/p&gt;

&lt;p&gt;Both approaches are viable in different scenarios, and which you use depends on the context of the functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it right
&lt;/h2&gt;

&lt;p&gt;The next layer of focus is making the code right. &lt;em&gt;Right&lt;/em&gt; means making the code readable and maintainable. All of the tests written up to this point pay off by giving you confidence that functionality remains.&lt;/p&gt;

&lt;p&gt;Developers read code much more than write it, so taking the time to make your code readable will help your colleagues and your future self. Readable code is more maintainable and extendable. The small cost of refactoring now can have an exponential impact on future project development.&lt;/p&gt;

&lt;p&gt;Focus on small changes that make a noticeable impact. Use domain-specific variable names, ensure your functions are in the correct scope, use comments as indicators for creating functions. I recommend reading Martin Fowler's excellent book &lt;a href="https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599/ref=pd_lpo_14_t_0/135-0301302-4311004?_encoding=UTF8&amp;amp;pd_rd_i=0134757599&amp;amp;pd_rd_r=b0b344db-197c-486e-844c-ca016c5ce3de&amp;amp;pd_rd_w=3V0TA&amp;amp;pd_rd_wg=3BK50&amp;amp;pf_rd_p=16b28406-aa34-451d-8a2e-b3930ada000c&amp;amp;pf_rd_r=S5WMMCDX49SEKYDQBFEN&amp;amp;psc=1&amp;amp;refRID=S5WMMCDX49SEKYDQBFEN"&gt;Refactoring&lt;/a&gt; for more specific guidance and practical advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it fast (if needed)
&lt;/h2&gt;

&lt;p&gt;After we've made the code work, considered edge cases, added fallback support, and made the code readable, we finally optimize performance. Maybe.&lt;/p&gt;

&lt;p&gt;The first rule from the &lt;a href="https://wiki.c2.com/?RulesOfOptimization"&gt;Rules of Optimization&lt;/a&gt; is don't optimize. If you must optimize, however, there are a few things to keep in mind.&lt;/p&gt;

&lt;p&gt;First, make sure you are testing production code. Many frameworks bolt-on functionality and tools that help the development process but hinder performance. Production builds exclude this extra functionality, so ensuring you are testing in the right environment keeps you from optimizing performance unnecessarily.&lt;/p&gt;

&lt;p&gt;Next, you need to get baseline measurements. These will ensure that your optimizations worked. If possible, emulate slow internet connections and throttle CPU speed, two features &lt;a href="https://developers.google.com/web/tools/chrome-devtools/evaluate-performance"&gt;Chrome's devtools&lt;/a&gt; offers.&lt;/p&gt;

&lt;p&gt;One thing to keep in mind as you optimize is how your optimizations may affect the readability and maintainability of code. Usually, the most optimized solution is nowhere near the most readable. Always favor readable code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Performance optimization makes sense to developers because it's something that's easily measured. Optimizing code may be a more attractive goal than finding edge cases or making code more readable but should be avoided until the end of the process. Optimizing at the very end forces developers to focus on writing working, maintainable code.&lt;/p&gt;

&lt;p&gt;So, thanks for making it to the end! If you found this helpful, please let me know by liking this article on DEV Community and following me to know when I post a new article. And ask me all the questions you'd like (preferably about this article, but no judgment) on &lt;a href="https://twitter.com/therealboone"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>career</category>
      <category>javascript</category>
      <category>process</category>
      <category>programming</category>
    </item>
    <item>
      <title>What I learned blogging daily for a month</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Thu, 04 Mar 2021 18:05:40 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/what-i-learned-blogging-daily-for-a-month-2ff8</link>
      <guid>https://dev.to/falldowngoboone/what-i-learned-blogging-daily-for-a-month-2ff8</guid>
      <description>&lt;p&gt;I recently challenged myself to &lt;a href="https://www.falldowngoboone.com/blog/blogruary-28-days-of-posting/"&gt;write a blog post every day for a month&lt;/a&gt;. Up to that point, I had only written eight blog posts for two years. A month later, not only do I have 28 new blog posts, I've learned some valuable lessons about my blogging process and my website. I wanted to share things about the experience that worked for me and issues I discovered that need attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked: Setting deadlines
&lt;/h2&gt;

&lt;p&gt;It's surprising how an unmoving date can motivate you to complete a post. One of my recurring issues with blogging has been the tendency to overwork a piece until I completely lose interest. Experience has taught me that time is the biggest enemy of posting, and firm deadlines force you to throw away what's not working and focus on what is.&lt;/p&gt;

&lt;p&gt;Being accountable to others dramatically increases the power of deadlines. One of my blogging rules involved announcing in my Twitter feed that I was posting daily. In my mind, I felt like I was accountable to a large audience. Social pressure works, even if it's imaginary&lt;sup id="fnref1"&gt;1&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked: Having a healthy backlog
&lt;/h2&gt;

&lt;p&gt;In the middle of February, we experienced the second coldest temperatures ever recorded in North Texas (-2℉, -17℃). That may not be cold for some of you reading this, but our state's infrastructure is poorly prepared for temperatures that cold. Power generation froze up, and several utility buildings lost power, including our water station. Some were without power and water for a week.&lt;/p&gt;

&lt;p&gt;I barely wrote during that week, yet I stayed on schedule thanks to my backlog, a running list of ideas in Notion that I use to build out full posts. Sometimes an article I've been working on just isn't working, so it's nice to have a safety net.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worked: Cross-publishing on DEV
&lt;/h2&gt;

&lt;p&gt;I decided at the suggestion of &lt;a href="https://twitter.com/5t3ph"&gt;Stephanie Eckles&lt;/a&gt; to cross-publish to DEV Community. Cross-publishing ended up being a great decision because DEV's platform exposed me to a bigger audience than I ever would've received on my blog. Coupled with the motivation and encouragement I received from comments and reader interaction, I've decided to continue doing this for all posts.&lt;/p&gt;

&lt;p&gt;There are, however, some challenges I've found cross-publishing, mainly due to differences in platforms. My site is built in Eleventy, and my front matter formatting doesn't completely align with DEV's. There are also differences in how I mark up embedded content. If you notice, hardly any of my posts have CodePens embedded, and I didn't use any images.&lt;/p&gt;

&lt;p&gt;Now that I have some time, I'm going to start ironing out these issues, starting with images. That's a topic I need to address on my site first, which brings me to my first thing that didn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did not work: My website styling is lacking
&lt;/h2&gt;

&lt;p&gt;In terms of my blog, &lt;a href="https://www.falldowngoboone.com"&gt;falldowngoboone&lt;/a&gt;, I discovered many rough edges throughout the month. One such issue that comes to mind is an excerpt filter I created&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, which unfortunately doesn't process Markdown properly. If anything in the first 20 words of a post contains Markdown, it's not processed, and if there are any special characters, they are escaped.&lt;/p&gt;

&lt;p&gt;It also appears some of the code blocks that Notion produces use a syntax I don't support. I know that may not seem like a big deal, but it's broken syntax highlighting on my site. This should be an easy fix.&lt;/p&gt;

&lt;p&gt;Finally, my home page needs an overhaul. When I had only eight posts, it was easy to find them. Now, it's getting a bit ridiculous. I plan to completely change the homepage design, featuring the most popular articles, my most recent posts, and a link to post archives. I may even add a search (gasp!) at some point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did not work: My publishing process sucks
&lt;/h2&gt;

&lt;p&gt;Near the end of the month, I wrote about &lt;a href="https://www.falldowngoboone.com/blog/how-i-write-my-posts/"&gt;how I write my posts&lt;/a&gt;, and I briefly mentioned some ideas I had for optimizing my publishing flow. Currently, I walk through at least 18 different steps to publish a single blog piece. I think better tools and well-planned automation can reduce those steps.&lt;/p&gt;

&lt;p&gt;Notion, as I'm currently using it, doesn't fit with my publishing process. Unfortunately, its exported Markdown output doesn't align with the format I use in Eleventy. It would be nice if Notion offered a way to tweak the export formats, but I understand that constraints like this are necessary to allow flexibility in other areas.&lt;/p&gt;

&lt;p&gt;One idea I have is to write a script that could convert the exported metadata into front matter. Another idea is to change to a full content management solution, like Sanity. Scripting a solution would mean I could keep using Notion, with the downside of maintenance. Using Sanity would give me a streamlined publishing solution with little toil. I'm still weighing my options here (and I'll make sure to write about whatever choice I make).&lt;/p&gt;

&lt;p&gt;Finally, there are areas of my process best suited for automation. My article backlog includes a status flag (Planned, In Progress, and Published&lt;sup id="fnref3"&gt;3&lt;/sup&gt;). I'd love to figure out a way to automate In Progress when I schedule the piece and Published once the post has merged.&lt;/p&gt;

&lt;h2&gt;
  
  
  What next?
&lt;/h2&gt;

&lt;p&gt;I will not continue to blog daily, as insightful as it's been. But I will focus on creating blog posts at a regular frequency, at least once a week. I hope to improve my communication skills and connect with more amazing people through DEV Community.&lt;/p&gt;

&lt;p&gt;Finally, if you're thinking about blogging, do it. Don't worry about whether or not you have something to say. When I began blogging, I was afraid I wouldn't have anything to say. Now I can't stop writing.&lt;/p&gt;

&lt;p&gt;That's it for now. If you found this helpful, please let me know by liking this post on DEV Community and following me so you know when I publish. And if you have any questions, don't hesitate to reach out to me on Twitter.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Most popular posts
&lt;/h2&gt;

&lt;p&gt;In case you're interested, here are my most popular articles from February:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/falldowngoboone/how-to-avoid-premature-abstraction-in-react-5672"&gt;How to avoid premature abstraction in React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/falldowngoboone/share-variables-between-javascript-and-css-5ad0"&gt;Share variables between JavaScript and CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/falldowngoboone/how-i-write-my-posts-4538"&gt;How I write my posts&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If you'd like to learn more about different techniques to help you become consistent at blogging or any other habit, check out &lt;em&gt;Atomic Habits&lt;/em&gt; by James Clear. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;In case you didn't know, fdgb runs on &lt;a href="https://www.11ty.dev"&gt;Eleventy&lt;/a&gt;, a JavaScript-based static site generator that's a joy to use. Filters in Eleventy act like functions that can transform your blog's content. They are simple yet powerful. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;I'm not completely happy with these names, but they work for now. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>blogging</category>
      <category>career</category>
    </item>
    <item>
      <title>Share variables between JavaScript and CSS</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Mon, 01 Mar 2021 02:16:29 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/share-variables-between-javascript-and-css-5ad0</link>
      <guid>https://dev.to/falldowngoboone/share-variables-between-javascript-and-css-5ad0</guid>
      <description>&lt;p&gt;Whether you need site breakpoints for &lt;code&gt;matchMedia&lt;/code&gt; or access to theme colors, sharing values between your JavaScript and CSS is sometimes unavoidable. The easiest solution is to &lt;a href="https://www.falldowngoboone.com/blog/understand-the-context-of-code-you-copy/"&gt;copy and paste&lt;/a&gt; values, but how can you ensure values stay synchronized when that brand blue color changes to indigo?&lt;/p&gt;

&lt;p&gt;The answer is to create a single source of truth by sharing values between the JavaScript and style layers. There are several ways to accomplish this, and the best approach for a project depends on its frontend stack. Here are all of the ways I know how to pass data back and forth between all the layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  ICSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/css-modules-part-1-need/"&gt;CSS Modules&lt;/a&gt;, gives us two ways of sharing variables, the &lt;a href="https://github.com/css-modules/icss"&gt;Interoperable CSS&lt;/a&gt; (ICSS) spec and the PostCSS Modules Values spec. ICSS appears to be the older of the two specifications, so I'll start there.&lt;/p&gt;

&lt;p&gt;ICSS is a low-level specification that's mainly for loader authors. It describes how to treat CSS modules as JavaScript dependencies and introduces the &lt;code&gt;:export&lt;/code&gt; directive to act as a way to export defined values. Coupled with Sass variables, it allows you to export theme values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="c1"&gt;// colors.module.scss&lt;/span&gt;
&lt;span class="c1"&gt;// assuming this is within Create React App; the `.module` lets CRA know&lt;/span&gt;
&lt;span class="c1"&gt;// this is a CSS Module&lt;/span&gt;

&lt;span class="nv"&gt;$my-red&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ff0000&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;

&lt;span class="na"&gt;:export&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;myRed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$my-red&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exported values are imported like much like any other JavaScript module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MyComponent.js&lt;/span&gt;
&lt;span class="c1"&gt;// assuming this is within Create React App&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme/colors.module.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;myRed's value: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myRed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above should work in Create React App out of the box. If you are rolling your own Webpack configuration (may God have mercy on your soul), you'll need to &lt;a href="https://github.com/webpack-contrib/css-loader#compiletype"&gt;configure &lt;code&gt;modules&lt;/code&gt; with a &lt;code&gt;compileType&lt;/code&gt; of &lt;code&gt;icss&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;css-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;compileType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;icss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;CSS is the source of truth&lt;/li&gt;
&lt;li&gt;Great solution if you're using Create React App&lt;/li&gt;
&lt;li&gt;Webpack configuration could be a challenge&lt;/li&gt;
&lt;li&gt;Aimed more at library authors than app developers&lt;/li&gt;
&lt;li&gt;Non-standard CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting &lt;code&gt;@value&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;CSS Modules also offers &lt;a href="https://github.com/css-modules/postcss-modules-values"&gt;the &lt;code&gt;@value&lt;/code&gt; directive&lt;/a&gt;, which explicitly defines module values. &lt;code&gt;@value&lt;/code&gt; can also be used to import values from other CSS modules. It's a catchall solution for passing values to and from CSS modules to anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sass"&gt;&lt;code&gt;&lt;span class="c1"&gt;// breakpoints.module.css&lt;/span&gt;

&lt;span class="k"&gt;@value&lt;/span&gt; &lt;span class="nt"&gt;larry&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;max-width&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;599px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@value&lt;/span&gt; &lt;span class="nt"&gt;moe&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;min-width&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;600px&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;and&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;max-width&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;959px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@value&lt;/span&gt; &lt;span class="nt"&gt;curly&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;min-width&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;960px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// MyComponent.module.css&lt;/span&gt;
&lt;span class="c1"&gt;// this is one of the multiple ways you can import @value definitions&lt;/span&gt;
&lt;span class="c1"&gt;// see https://github.com/css-modules/postcss-modules-values&lt;/span&gt;

&lt;span class="k"&gt;@value&lt;/span&gt; &lt;span class="nt"&gt;larry&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;moe&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;curly&lt;/span&gt; &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;"theme/breakpoints.module.css"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;larry&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;moe&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;curly&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MyComponent.module.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Media&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-media&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;larry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curly&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;bp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme/breakpoints.module.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Media&lt;/span&gt; &lt;span class="na"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;larry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;moe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;curly&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;larry&lt;/span&gt; 
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Oh, a wise guy, eh?&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt; 
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moe&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Why I outta...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Nyuk nyuk&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Media&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;CSS is the source of truth&lt;/li&gt;
&lt;li&gt;Easy to implement in Create React App&lt;/li&gt;
&lt;li&gt;Again, Webpack (meh)&lt;/li&gt;
&lt;li&gt;Appears to be more of a dev-friendly solution&lt;/li&gt;
&lt;li&gt;Allows you to share values between CSS modules&lt;/li&gt;
&lt;li&gt;Non-standard CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sass compiler
&lt;/h2&gt;

&lt;p&gt;Sass's JavaScript API can add custom functions by defining the &lt;a href="https://sass-lang.com/documentation/js-api#functions"&gt;&lt;code&gt;functions&lt;/code&gt; option of &lt;code&gt;render&lt;/code&gt;&lt;/a&gt;. You can use this to define getter functions that return your theme's values directly in Sass. I implemented this on our website using &lt;code&gt;node-sass&lt;/code&gt;, which exposes the &lt;code&gt;functions&lt;/code&gt; option in its CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node-sass src/styles/ &lt;span class="nt"&gt;-o&lt;/span&gt; build/styles &lt;span class="nt"&gt;--functions&lt;/span&gt; path/to/sass-functions.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;sass-functions.js&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sass-functions.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-sass&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../theme/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getColorMap()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;toSassMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getMqMap()&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mqs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;toSassMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mqs&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;toSassMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I'm having to define Sass types. The &lt;code&gt;getColorMap()&lt;/code&gt; and &lt;code&gt;getMqMap()&lt;/code&gt; functions return Sass maps that include all of our theme variables. Very handy!&lt;/p&gt;

&lt;p&gt;Unfortunately, &lt;a href="https://sass-lang.com/blog/libsass-is-deprecated"&gt;LibSass, the core engine of &lt;code&gt;node-sass&lt;/code&gt;, has been deprecated&lt;/a&gt;, along with &lt;code&gt;node-sass&lt;/code&gt;. The canonical Dart version of Sass lacks a nice CLI option for custom functions. If you want to recreate this functionality, you're stuck building a compiler using Dart Sass's JavaScript API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript (or JSON) is the source of truth&lt;/li&gt;
&lt;li&gt;Requires Sass&lt;/li&gt;
&lt;li&gt;Easy to implement with &lt;code&gt;node-sass&lt;/code&gt;, but it's deprecated&lt;sup id="fnref1"&gt;1&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;You have to role your own compilation if you want to define custom functions in Dart Sass&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;A common variable sharing solution in React is to simply let JavaScript do all the work. And as controversial as &lt;a href="https://css-tricks.com/the-differing-perspectives-on-css-in-js/"&gt;CSS-in-JS&lt;/a&gt; seems to be, it presents an easy way to share variables simply because you are defining CSS in a JavaScript file.&lt;/p&gt;

&lt;p&gt;Here's how you might share a variable in the library &lt;a href="https://emotion.sh/docs/introduction"&gt;Emotion&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cx&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;colors&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./theme.colors.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
      color: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;
      &amp;amp;:hover {
        color: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;
      }
    `&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    I get so emotional, baby.
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mean, it's easy. It's so easy that I'd debate whether or not this is classified as sharing variables between JavaScript and CSS, but I'm throwing it in anyway.&lt;/p&gt;

&lt;p&gt;I've already mentioned Emotion, but other CSS-in-JS libraries to check out include &lt;a href="https://styled-components.com"&gt;Styled Components&lt;/a&gt;, &lt;a href="https://cssinjs.org/"&gt;JSS&lt;/a&gt;, &lt;a href="https://theme-ui.com"&gt;Theme-UI&lt;/a&gt;, &lt;a href="https://formidable.com/open-source/radium/"&gt;Radium&lt;/a&gt;, and &lt;a href="https://github.com/Khan/aphrodite"&gt;Aprhodite&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript (or JSON) is the source of truth&lt;/li&gt;
&lt;li&gt;Good option for shared React component libraries&lt;/li&gt;
&lt;li&gt;Requires JavaScript to apply styles (no JS, no styles)&lt;/li&gt;
&lt;li&gt;Non-standard CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom Properties
&lt;/h2&gt;

&lt;p&gt;If you need a lightweight, "proper" way to share variables between JavaScript and CSS, look no further than Custom Properties. Custom Properties allow you to create arbitrary CSS properties and set them to any value you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#BADA55&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#005DAB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;brandColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPropertyValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--color-brand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;brandColor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;I'm brand color!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you access these properties a lot, you may want to create a utility function to save on typing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;cssValue&lt;span class="o"&gt;(&lt;/span&gt;property&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;getComputedStyle&lt;span class="o"&gt;(&lt;/span&gt;document.documentElement&lt;span class="o"&gt;)&lt;/span&gt;
    .getPropertyValue&lt;span class="o"&gt;(&lt;/span&gt;property&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Custom Properties are completely standard CSS spec, and this is the only solution that is dynamic, meaning properties can change based on context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#BADA55&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#005DAB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color-brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you access those properties, they'll be different depending on the user's preferences. This is incredibly powerful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS is the source of truth&lt;/li&gt;
&lt;li&gt;Context-specific (CSS updates custom properties live based on the context)&lt;/li&gt;
&lt;li&gt;Easy to implement&lt;/li&gt;
&lt;li&gt;Not supported in Internet Explorer 11, but you can sort of &lt;a href="https://www.npmjs.com/package/ie11-custom-properties"&gt;polyfill&lt;/a&gt; it&lt;/li&gt;
&lt;li&gt;Can't use Custom Properties for breakpoints 😩&lt;/li&gt;
&lt;li&gt;Standard CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lots of options
&lt;/h2&gt;

&lt;p&gt;Sharing variables between CSS and JavaScript can help reduce toil and cut down on unintentional tech debt. And if you need to, you have no shortage of options. Just make sure you understand what you want to act as the source of truth and know your technical requirements.&lt;/p&gt;

&lt;p&gt;If you found that helpful, please let me know by liking this post on DEV Community and sharing it. And if you want more articles like this, make sure you &lt;a href="https://twitter.com/therealboone"&gt;follow me on Twitter&lt;/a&gt; so you'll know when I post new ones.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Trust me when I say you should not use &lt;code&gt;node-sass&lt;/code&gt;. It's so buggy that it can't process newer CSS. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>sass</category>
    </item>
    <item>
      <title>How I write my posts</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Sat, 27 Feb 2021 20:13:50 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/how-i-write-my-posts-4538</link>
      <guid>https://dev.to/falldowngoboone/how-i-write-my-posts-4538</guid>
      <description>&lt;p&gt;I've been posting a &lt;a href="https://www.falldowngoboone.com/blog/blogruary-28-days-of-posting/"&gt;new article every day this month&lt;/a&gt;, and it's been interesting. I'll write about that soon, but today I want to share how I put these posts together. Hopefully, you'll learn something new from my process, or see how terrible my process is and give me some advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idea backlog
&lt;/h2&gt;

&lt;p&gt;If you're going to create content, you need a steady stream of ideas. I keep a backlog so I can store notes, write drafts, and constantly reassess what to post next. This post you're reading? It started out in my backlog.&lt;/p&gt;

&lt;p&gt;You can keep an idea backlog in whatever tool you use. I use &lt;a href="https://www.notion.so/"&gt;Notion&lt;/a&gt; for a few reasons. First, I can access Notion on anything that's connected to the internet, so I can capture an idea before I forget it. Second, Notion gives me the flexibility to organize my content however I want.&lt;/p&gt;

&lt;p&gt;For Blogruary, I created a special post in Notion with a list view of all posts tagged "blograry" (each view can have its own special filtering and sorting). This allows me to get a quick look at all the ideas available to me.&lt;/p&gt;

&lt;p&gt;For more information on using Notion as a project manager, check out this &lt;a href="https://youtu.be/32dLXdB4ozs"&gt;Notion masterclass video from Thomas Frank&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the post
&lt;/h2&gt;

&lt;p&gt;After adding an idea to the backlog, I paste links to relevant material as well as notes into the created entry. This eventually becomes the blog post itself. Depending on how far along the information is, I'll go ahead and schedule the publish date.&lt;/p&gt;

&lt;p&gt;If I'm being honest, this process has been all over the place. Sometimes I'll start the post without any notes, or the post is a long list of notes until the day I have to post. Many times I'm stuck writing and editing the entire post the day I'm supposed to publish (like today).&lt;/p&gt;

&lt;p&gt;I've also noticed some issues with writing blog posts in Notion, mainly because of all the manual changes I have to make to the exported Markdown. I'm still trying to figure this part of the process out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing
&lt;/h2&gt;

&lt;p&gt;After I've written a post, I'll export it from Notion as Markdown, drop it into my blog's source code, and manually format the &lt;a href="https://www.11ty.dev/docs/data-frontmatter/"&gt;front matter for Eleventy&lt;/a&gt;. Unfortunately, Notion doesn't output front matter, nor does it allow me to customize how associated data is displayed. This would be a welcome feature.&lt;/p&gt;

&lt;p&gt;I've started to run the post content through &lt;a href="https://app.grammarly.com"&gt;Grammarly&lt;/a&gt; to correct any grammar mistakes. Grammarly has extensions for all the major browsers, as well as a desktop app that allows you to create a new document for analysis (big thanks to &lt;a href="https://twitter.com/5t3ph"&gt;Stephanie Eckles&lt;/a&gt; for that tip). It's free to use the basic features, but I'm considering upgrading.&lt;/p&gt;

&lt;p&gt;After making corrections in Grammarly, I'll paste the corrections back into the Markdown file in VS Code, commit the changes, and push them up to GitHub. &lt;/p&gt;

&lt;p&gt;After creating a pull request, &lt;a href="https://www.netlify.com"&gt;Netlify&lt;/a&gt; runs a series of checks. While that happens, I create a duplicate of the post on DEV Community. I have to add a &lt;a href="https://dev.to/michaelburrows/comment/125j0"&gt;canonical URL&lt;/a&gt; back to my website so I don't get penalized for duplicate content, but in order to do that, I need to publish on &lt;a href="https://www.falldowngoboone.com"&gt;my personal blog&lt;/a&gt; first.&lt;/p&gt;

&lt;p&gt;By the time I've set up a draft in DEV, my pull request has been verified and is ready to merge. The merge kicks off a deployment to Netlify (yay Netlify!). After merging, I grab the post's URL, add it to the DEV post, then I hit publish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thoughts on the process
&lt;/h2&gt;

&lt;p&gt;So that's what I've been doing this entire month. I learned that Notion is a great tool for organizing and writing, but I need a tool that's closer to the end product. I'm starting to look at &lt;a href="https://www.sanity.io"&gt;Sanity&lt;/a&gt; as a viable option. &lt;/p&gt;

&lt;p&gt;I've also been thinking about streamlining the publishing process by automating the duplicate post to DEV. Challenges include differences in front matter and markup. If I can limit myself to only shared markup, that would make the automation much easier.&lt;/p&gt;

&lt;p&gt;Finally, if you're thinking about starting a blog, a good place to start is signing up for the &lt;a href="https://bloggingfordevs.com"&gt;Blogging for Devs&lt;/a&gt; newsletter from Monica Lent. New subscriptions start with a 7-day blogging course sent directly to your inbox, followed by regular emails filled with tips for content makers.&lt;/p&gt;

&lt;p&gt;I will be expanding on the rest of what I've learned in a future post, so if you want to know when that comes out, follow &lt;a href="https://twitter.com/therealboone"&gt;me on Twitter&lt;/a&gt;. And if you found this post helpful, please let me know by liking it on DEV Community.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>career</category>
    </item>
    <item>
      <title>Understand the context of code you copy</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Fri, 26 Feb 2021 23:43:14 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/understand-the-context-of-code-you-copy-527b</link>
      <guid>https://dev.to/falldowngoboone/understand-the-context-of-code-you-copy-527b</guid>
      <description>&lt;p&gt;If a problem is too difficult to solve on our own, sometimes the only course of action is to search, copy, and paste. It's something all developers have done regardless of their experience level.&lt;/p&gt;

&lt;p&gt;While there is no shame in copying and pasting code, it can lead to a mess. That's why I have one copy-paste rule that I follow: &lt;strong&gt;understand the context.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A reason for everything
&lt;/h2&gt;

&lt;p&gt;Requirements, constraints, and assumptions all play a role in how we write code. Unless you've found a generic solution, the code you copy comes with a context that may not apply to your situation.&lt;/p&gt;

&lt;p&gt;The wrong context could cause you to miss story requirements or cause other code to break (e.g. variable name collisions). Taking time to understand the context can help save you and your teammates a headache down the road.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning the context
&lt;/h2&gt;

&lt;p&gt;Throughout my experience of dealing with copied and pasted code, I've seen a few common problems. These can be avoided by asking yourself some questions as you &lt;em&gt;carefully read the code.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Does this code rely on global state?&lt;/strong&gt; This is a common issue with legacy JavaScript codebases that rely on the global Window object to pass data and functions around. If you can, move this code out of global scope. It will help you answer the remaining questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there anything that looks unfamiliar?&lt;/strong&gt; Are there APIs used that don't look familiar to you? Look them up and make sure you understand what they are used for and why. Identifying unfamiliar APIs and methods informs the code's context, but it's also an opportunity for you to learn something new.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there anything unnecessary?&lt;/strong&gt; Dead code in a large codebase is scary. No one will touch it, either out of fear or because of the amount of work involved in tracking down usage. Hastily copying and pasting dead code multiplies this tech debt immeasurably. Trim unused code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do variable names match the domain?&lt;/strong&gt; If you're copying from an online source, variables will tend to have generic names. Taking the time to find better names &lt;a href="https://www.falldowngoboone.com/blog/what-is-your-code-communicating/"&gt;helps the code communicate better&lt;/a&gt;, and captures the context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does this code have any dependencies?&lt;/strong&gt; There are swaths of our codebase that use Require.js and therefore...require...it to be present. Sometimes it's better to remove the need for that dependency by copying and pasting even more code, and sometimes it's better to add the dependency. It all &lt;em&gt;depends&lt;/em&gt; on the &lt;em&gt;context&lt;/em&gt; (ah, see what I did there?).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are there any new errors logged after pasting?&lt;/strong&gt; The moment you paste code into your codebase, you should check your logging. This reduces the &lt;a href="https://www.falldowngoboone.com/blog/the-feedback-loop/"&gt;dev feedback loop&lt;/a&gt; and informs you of any incompatibilities immediately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  This looks like a lot of work
&lt;/h2&gt;

&lt;p&gt;You may be thinking, &lt;em&gt;Geez, Ryan, this sounds like it's going to take forever, and the reason I'm copying and pasting code is to save time.&lt;/em&gt; And you're right, this will add time to your solution. But that extra time will ensure that you solve the right problem, meet requirements, and reduce tech debt.&lt;/p&gt;

&lt;p&gt;Copying existing code can supercharge your team's velocity, but remember this comes with a cost. If you don't slow down and ask questions, chances are you'll pay the price further down the road, and you'll lose any short-term gains over the long term.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take the time to save time
&lt;/h2&gt;

&lt;p&gt;Sometimes copying and pasting existing code is the best course of action. But before do, take the time to understand and adapt the context. The time you spend will save you in the long run.&lt;/p&gt;

&lt;p&gt;If you're still reading this, thanks for sticking around to the end! Unless you just skipped to the end. In which case, thanks for at least reading this? If you found this post helpful, please consider liking over on DEV Community, and make sure you &lt;a href="https://twitter.com/therealboone"&gt;follow me&lt;/a&gt; so you know when I release a new post.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>13 fantastic web development blogs</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Thu, 25 Feb 2021 18:54:53 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/13-fantastic-web-development-blogs-37ec</link>
      <guid>https://dev.to/falldowngoboone/13-fantastic-web-development-blogs-37ec</guid>
      <description>&lt;p&gt;There are some amazing web development blogs out there (&lt;a href="https://www.smashingmagazine.com"&gt;Smashing Magazine&lt;/a&gt;, &lt;a href="https://css-tricks.com"&gt;CSS-Tricks&lt;/a&gt; and &lt;a href="http://tympanus.net/codrops/"&gt;Codrops&lt;/a&gt; come to mind), but today I want to pay homage to the humble personal blog. Here are some of my favorite web developer blogs in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://moderncss.dev"&gt;Modern CSS&lt;/a&gt; - Do you still struggle with vertical centering in CSS? This series from Stephanie Eckles is for you.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://danmall.me"&gt;Dan Mall&lt;/a&gt; - Dan, the founder of the design collaborative &lt;a href="https://superfriendlydesign.systems"&gt;SuperFriendly&lt;/a&gt;, is a generous source for all things design systems and client relations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stuffandnonsense.co.uk/blog"&gt;Stuff &amp;amp; Nonsense&lt;/a&gt; - Andy Clark has been building websites for a while, and he has a thing or two to teach about writing bulletproof CSS. His &lt;em&gt;Inspired Design Decisions&lt;/em&gt; series is a must-read.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.sarasoueidan.com/blog/"&gt;Sara Soueidan&lt;/a&gt; - I started following Sara a while back thanks to her amazing contributions to the Codrops blog. She is currently focusing on accessibility, but her SVG filter articles will blow your mind.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sarahmhigley.com"&gt;Sarah Higley&lt;/a&gt; - Sarah is a developer at Microsoft with a passion for accessibility. I learned about her through the fantastic A11y Slack group.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://adrianroselli.com"&gt;Adrian Roselli&lt;/a&gt; - I read Adrian's blog to learn how bad I am at accessibility.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://benmyers.dev"&gt;Ben Meyers&lt;/a&gt; - I am lucky to know Ben personally through a local Meetup. His blog is filled with tons of great accessibility information. Oh, and make sure you check out his Twitch channel &lt;a href="https://www.twitch.tv/someanticsdev"&gt;Some Antics&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.joshwcomeau.com"&gt;Josh Comeau&lt;/a&gt; - In addition to posting some great frontend content, Josh has created a truly fun website. I want my blog to be like Josh's when it grows up.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://frankchimero.com"&gt;Frank Chimero&lt;/a&gt; - Frank is an accomplished web designer and writer. This site features dense engaging writing that will make you think. My favorite is &lt;em&gt;&lt;a href="https://frankchimero.com/blog/2015/the-webs-grain/"&gt;The Web's Grain&lt;/a&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bradfrost.com"&gt;Brad Frost&lt;/a&gt; - He created Atomic Design. He plays bass. If you want to dive more into design systems, you need to follow Brad.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://markboulton.co.uk"&gt;Mark Boulton&lt;/a&gt; - Mark's writing has helped me bridge the gap between print and screen design. I highly recommend for developers &lt;em&gt;and&lt;/em&gt; designers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://2ality.com"&gt;2ality&lt;/a&gt; - Dr. Axel Rauschmayer's blog is a JavaScript developer's dream. He features deep dives into the ES spec and explorations into brand-new JavaScript features.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rachelandrew.co.uk"&gt;Rachel Andrew&lt;/a&gt; - Rachel is an accomplished speaker, writer, and web developer. If you are passionate about CSS, this blog's for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Didn't see your favorite? Please &lt;a href="https://twitter.com/therealboone"&gt;let me know on Twitter&lt;/a&gt; who I need to add. And if you just discovered a new blog to follow from this post, please let me know by liking this on DEV Community. Likes, much like food, sustain me and give me the strength I need to face the day.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>How to get your pull request merged</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Wed, 24 Feb 2021 21:45:23 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/how-to-get-your-pull-request-merged-539h</link>
      <guid>https://dev.to/falldowngoboone/how-to-get-your-pull-request-merged-539h</guid>
      <description>&lt;p&gt;When I started my first job as a professional developer, one of my biggest challenges was learning how to create helpful pull requests. I had been using git for a few years at that point, but my experience was limited to merging and pushing directly to the default remote branch. As a result, my first code reviews were fraught with answering questions, rewriting code, and (gulp) interactive rebasing.&lt;/p&gt;

&lt;p&gt;What follows is what I've learned from the past few years of crafting pull requests. My hope is they'll help you whether you're starting your first job in development or trying to contribute to your favorite open source project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use atomic commits
&lt;/h2&gt;

&lt;p&gt;Effective pull requests start with &lt;a href="https://sparkbox.com/foundry/atomic_commits_with_git"&gt;atomic commits&lt;/a&gt;. Atomic commits add value, don't break tests, and are the smallest unit of work possible. A list of such commits may look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add product metadata HTML&lt;/li&gt;
&lt;li&gt;Style product metadata&lt;/li&gt;
&lt;li&gt;Add product metadata JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step in that list focuses on a particular domain of the overall task of adding product metadata, first the markup, then the CSS, and finally the behavior.&lt;/p&gt;

&lt;p&gt;Why use atomic commits? First of all, they're easier to read in a code review (more on that in a minute). Second, since they're self-contained, they're easy to revert without breaking anything.&lt;/p&gt;

&lt;p&gt;Now that we have well-structured commits, we need to think about the commit message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow commit message standards
&lt;/h2&gt;

&lt;p&gt;As anyone who has ever plugged a lamp in a wall can tell you, standards are important&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. Different projects may have different commit message standards. Make sure you read and understand them before you contribute.&lt;/p&gt;

&lt;p&gt;If you can't find any standards, here's a great article on &lt;a href="https://chris.beams.io/posts/git-commit/"&gt;how to write commit messages&lt;/a&gt;. My work more or less follows these standards. Here's a high-level summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The subject line should be 50 characters or less to prevent wrapping&lt;/li&gt;
&lt;li&gt;The subject line should be a statement written in &lt;a href="https://www.grammarly.com/blog/imperative/"&gt;imperative voice&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a commit message two carriage returns after the subject line&lt;/li&gt;
&lt;li&gt;Commit message lines should be limited to 72 characters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here's an example commit following these guidelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add new awesome feature

Use this space for anything that needs further explanation. You can 
add a list here as well:

- Added the thing
- Had to pull the other thing out into a separate module for reasons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's hard to know what your line character count is when staring into the black abyss of the terminal so you may want to change your default text editor to something you're more comfortable with. I personally like to use VS Code as my editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; core.editor &lt;span class="s2"&gt;"code --wait"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that command in your terminal should set up VS Code as the default git editor. The above command assumes you're using git as your version control and have the &lt;a href="https://code.visualstudio.com/docs/editor/command-line"&gt;VS Code CLI&lt;/a&gt; installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it short and sweet
&lt;/h2&gt;

&lt;p&gt;I cannot tell you how many pull requests I've personally submitted with 20+ files changed and thousands of lines touched&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. If you've ever tried reviewing a PR like that, you'll know how exhausting it is to simply &lt;em&gt;read&lt;/em&gt; the entire thing, let alone reason about what's going on.&lt;/p&gt;

&lt;p&gt;Pull requests should be kept short. If they can't be kept short, use atomic commits and make sure to arrange your commits in a way that logically tells a story.&lt;/p&gt;

&lt;p&gt;Let's imagine we need to add a new form to a project that uses existing logic. The commit history might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reformat file to match standards
Refactor variable names for readability
Refactor form submission to extract logic
Add form markup
Style form
Share form logic with the new form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This history is arranged to tell a story. The beginning sets up the change with some reformatting and refactoring of existing files. The middle adds and styles the new form. Finally, the previous work is combined to complete the functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review and clarify if necessary
&lt;/h2&gt;

&lt;p&gt;Your commits are atomic, you've followed the project's standards, and you've arranged your commits in a logical order. The next step is to push your code up to the remote repository and review your pull request. I will usually save my pull request as &lt;a href="https://github.blog/2019-02-14-introducing-draft-pull-requests/"&gt;a draft on GitHub&lt;/a&gt; at this stage.&lt;/p&gt;

&lt;p&gt;After posting a draft pull request, step through each commit, look through the changes, and ask yourself if any potential areas could cause a reviewer to get tripped up. I recommend taking a break before so you can have fresh eyes.&lt;/p&gt;

&lt;p&gt;If you find any trip hazards, add a comment to explain why you did what you did. If you can't explain why you did what you did, this is a good time to say so and ask for any suggestions. These comments give reviewers much-needed context to help inform the review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Listen to the reviewer
&lt;/h2&gt;

&lt;p&gt;The final step to getting your pull request merged is listening to the reviewer. This can prove challenging sometimes, especially if the reviewer has difficulty communicating in a cordial manner (every team has that one dev...). Aim to receive and consider every comment with equal respect. &lt;/p&gt;

&lt;p&gt;Even if you disagree with the comment or request, there is always value in re-examining what you've written from someone else's perspective. That doesn't mean you should blindly do what the reviewer is suggesting. Sometimes it helps to get a second opinion. &lt;/p&gt;

&lt;p&gt;I have had to remind myself not to take each comment personally. In a discussion with &lt;a href="https://twitter.com/chantastic?lang=en"&gt;Chantastic&lt;/a&gt; at a local meetup, he suggested to hold off replying to a review until you can start with "Thank you." This will help put you in the right frame of mind when responding to a particularly harsh review.&lt;/p&gt;

&lt;h2&gt;
  
  
  In summary and summation
&lt;/h2&gt;

&lt;p&gt;Creating helpful pull requests is a useful skill to develop, whether you're contributing to a team or open source software. Creating atomic, standards-based commits in a reasonable order and taking the review process seriously will get you a long way to getting that new feature merged.&lt;/p&gt;

&lt;p&gt;If you'd like more information on how to make your pull requests the best they can be, check out the &lt;a href="https://opensource.creativecommons.org/contributing-code/pr-guidelines/"&gt;Creative Commons PR guidelines&lt;/a&gt;. And if you found this post helpful, please let me know by liking it over on DEV Community, and follow &lt;a href="https://twitter.com/therealboone"&gt;me on Twitter&lt;/a&gt; for random thoughts and occasional blog updates.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;True story. Did you know there are 15 different &lt;a href="https://www.worldstandards.eu/electricity/plugs-and-sockets/"&gt;outlet plug design standards&lt;/a&gt; in use throughout the world today? ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Let me assure you karma is a &lt;em&gt;thing&lt;/em&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>career</category>
    </item>
    <item>
      <title>Talk to your React components with custom events</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Tue, 23 Feb 2021 20:33:31 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/talk-to-your-react-components-with-custom-events-537b</link>
      <guid>https://dev.to/falldowngoboone/talk-to-your-react-components-with-custom-events-537b</guid>
      <description>&lt;p&gt;I build pages with both React and non-React components, and sometimes all these components need to talk to each other. Examples include opening a React modal when a customer clicks a button or updating a text block when a customer adds a product from a React stepper. There are many ways to do this, but in my opinion, the best way is to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent"&gt;custom events&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are custom events?
&lt;/h2&gt;

&lt;p&gt;Custom events are just like regular browser events (e.g. "click", "keyup", etc.) except they're manually created. You can create a simple synthetic event with a custom type using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/Event"&gt;&lt;code&gt;Event&lt;/code&gt; constructor&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to pass arbitrary data, you can use the &lt;code&gt;CustomEvent&lt;/code&gt; interface&lt;sup id="fnref2"&gt;2&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use the &lt;code&gt;document&lt;/code&gt; element as the single event handler for all custom events because it centralizes all event methods and decouples custom events from specific nodes on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a single entity to manage events makes this approach act like a browser-native &lt;a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern"&gt;publish-subscribe pattern&lt;/a&gt;. Benefits to this pattern include decoupling (previously mentioned) and scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example time!
&lt;/h2&gt;

&lt;p&gt;I've built an &lt;a href="https://stackblitz.com/edit/react-zqp3ot?file=src%2FApp.js"&gt;example app&lt;/a&gt; with &lt;a href="https://create-react-app.dev"&gt;Create React App&lt;/a&gt; to illustrate this. The &lt;code&gt;App&lt;/code&gt; component includes a modal built with &lt;a href="https://github.com/reactjs/react-modal"&gt;React Modal&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-modal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./style.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Trigger modal outside React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Custom events are AWESOME!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onRequestClose&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;I was opened by a modal outside of React. How cool is that?&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;isOpen&lt;/code&gt; prop determines the &lt;code&gt;Modal&lt;/code&gt; component open state. We then control this state using the &lt;code&gt;useState&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;We will create a button outside of the React component that opens the React app modal. Let's add the button to the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- public/index.html --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"open-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I'm outside React&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make things a bit easier and reduce event boilerplate, I've put our event functions into a module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// events.js&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEventOnce&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleEventOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleEventOnce&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;once&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could go crazy and make this look more like traditional pub-sub implementations&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, or you could completely emulate the &lt;a href="https://nodejs.org/api/events.html#events_class_eventemitter"&gt;&lt;code&gt;EventEmitter&lt;/code&gt;&lt;/a&gt; interface if you want. Here I've tried to capture the most common functions.&lt;/p&gt;

&lt;p&gt;Now that we have all the pieces in place we need to wire everything up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The next step is to publish an event when the open button is clicked. For this example app, I'm going to do that in the &lt;code&gt;index.js&lt;/code&gt; file Create React App provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./events&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;openButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openButton:click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've named the event &lt;code&gt;openButton:click&lt;/code&gt;. I typically follow a pattern of &lt;code&gt;subject:verb&lt;/code&gt;, mainly because that's what I learned way back in my jQuery days. A nice benefit of this pattern is it reduces the possibility of event name collisions.&lt;/p&gt;

&lt;p&gt;Finally, we'll listen for that event inside the &lt;code&gt;App&lt;/code&gt; component and set the &lt;code&gt;isOpen&lt;/code&gt; state to &lt;code&gt;true&lt;/code&gt; when it's triggered. Since adding event listeners is a side effect, we'll use &lt;code&gt;useEffect&lt;/code&gt; to do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Modal&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-modal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./events&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./style.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openButton:click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Trigger modal outside React&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Custom events are AWESOME!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Modal&lt;/span&gt; &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onRequestClose&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;I was opened by a modal outside of React. How cool is that?&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;closeModal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now it works (hopefully)! You can &lt;a href="https://stackblitz.com/edit/react-zqp3ot?file=src%2FApp.js"&gt;test it for yourself over on StackBlitz&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom events are awesome indeed
&lt;/h2&gt;

&lt;p&gt;Custom events are great when you need two completely separate entities to talk to each other, which is a common problem in UI design. Be aware, though, this pattern's not all sunshine and rainbows. Drawbacks include an increased difficulty of maintenance (ghost events, or published events that are no longer listened to) and a higher degree of reasoning (indeterminate order of execution).&lt;/p&gt;

&lt;p&gt;I hope I've at the very least piqued your interest in custom events, and maybe even given you a solution to a problem you're dealing with right now. If that's you, please do me a favor and like this article on DEV Community. And while you're at it, &lt;a href="https://twitter.com/therealboone"&gt;follow me on Twitter&lt;/a&gt; so I don't get lonely.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Note this code will not work in Internet Explorer (what does, amirite?). You will need to use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events#the_old-fashioned_way"&gt;old-fashioned event constructor&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The &lt;code&gt;CustomEvent&lt;/code&gt; constructor is also unsupported in Internet Explorer (whomp whomp). They are created the same way as &lt;code&gt;Event&lt;/code&gt;s, but initialize with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent"&gt;&lt;code&gt;initCustomEvent&lt;/code&gt;&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;One addition could be a method to remove all event listeners for a particular event. You would need to manually track listeners in an object since there's no way of directly accessing event listeners in native browser event handling. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Tips for vanilla JavaScript DOM manipulation</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Mon, 22 Feb 2021 21:30:45 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/tips-for-vanilla-javascript-dom-manipulation-edm</link>
      <guid>https://dev.to/falldowngoboone/tips-for-vanilla-javascript-dom-manipulation-edm</guid>
      <description>&lt;p&gt;If you need to go &lt;em&gt;au naturale&lt;/em&gt; with your JavaScript DOM manipulation, here are some tips for improving performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;DocumentFragment&lt;/code&gt;s to add multiple elements
&lt;/h2&gt;

&lt;p&gt;Here's one way you might add multiple DOM nodes to a mounted node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fruit-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fruitItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;fruitItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruitItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// page reflows every time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code works, but the issue is the page will reflow every time &lt;code&gt;appendChild&lt;/code&gt; is called. If you have a long list of items to add, you're going to end up in a serious performance bottleneck, and an unhappy boss. The solution is to use a &lt;code&gt;[DocumentFragment](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fruit-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crateDocumentFragment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fruitItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;fruitItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruitItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// no page reflow!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;appendChild&lt;/code&gt; method is only called once, and this makes browsers (and my boss) very happy.&lt;/p&gt;

&lt;h2&gt;
  
  
  But if you can, use &lt;code&gt;ParentNode.append&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You can think of the &lt;code&gt;[ParentNode.append](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append)&lt;/code&gt; method as &lt;code&gt;appendChild&lt;/code&gt; on steroids (sans the rage and adult acne). Unlike its puny cousin &lt;code&gt;appendChild&lt;/code&gt;, &lt;code&gt;append&lt;/code&gt; can take multiple nodes, automatically converts string arguments to text nodes, and it utilizes &lt;code&gt;DocumentFragment&lt;/code&gt; for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the `consts` are my way of letting you know this is newer...🙃&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fruit-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crateDocumentFragment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fruitItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;banana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;li&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;fruitItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most convenient way to add multiple nodes to a parent node. &lt;a href="https://caniuse.com/mdn-api_element_append"&gt;Support is great&lt;/a&gt; if you don't have to prop up Internet Explorer. Luckily, if you do, there's a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append#polyfill"&gt;polyfill&lt;/a&gt; for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create &lt;code&gt;DocumentFragment&lt;/code&gt;s from strings with &lt;code&gt;Range&lt;/code&gt;s
&lt;/h2&gt;

&lt;p&gt;Imagine a world where you want to create HTML from a string. You might do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// orange you getting tired of this example yet?&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fruit-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;li&amp;gt;apple&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;orange&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;banana&amp;lt;/li&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is nice if you're trying to recreate JSX, but it's not as performant as using &lt;code&gt;DocumentFragment&lt;/code&gt;s. Luckily there's a way to directly create a &lt;code&gt;DocumentFragment&lt;/code&gt; from a string. Contrived code warning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fruit-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRange&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;createContextualFragment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;li&amp;gt;apple&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;orange&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;banana&amp;lt;/li&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;createRange&lt;/code&gt; method returns a &lt;code&gt;Range&lt;/code&gt;, which is a representation of a sliver of the current DOM document. The &lt;code&gt;createContextualFragment&lt;/code&gt; creates a &lt;code&gt;DocumentFragment&lt;/code&gt; using a parsing algorithm based on the current document's context (in this case, HTML). &lt;code&gt;Range&lt;/code&gt; methods are meant to be convenience methods built on top of common node editing patterns with optimization in mind, and I'm quite interested to learn more about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memorize the DOM properties that trigger layout
&lt;/h2&gt;

&lt;p&gt;The DOM API is tricky because just observing certain node properties can trigger page layout. Doing this multiple times in a row can be a performance problem. Doing this inside a loop can cause &lt;a href="https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing"&gt;layout thrashing&lt;/a&gt; (trust me, it's as bad as it sounds).&lt;/p&gt;

&lt;p&gt;You'll want to be aware of what DOM properties cause the browser to trigger layout, so you need to get to memorizing. Or you could simply bookmark this convenient &lt;a href="https://gist.github.com/paulirish/5d52fb081b3570c81e3a"&gt;list of properties that cause layout&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is only scratching the proverbial surface
&lt;/h2&gt;

&lt;p&gt;There's more to DOM layout with vanilla JavaScript, to be sure. I'm interested in looking at some of the performance optimizations VDOM libraries employ to eek the most out of DOM manipulation. I kinda like that sort of thing.&lt;/p&gt;

&lt;p&gt;I hope you learned something new today. And if you did, please consider liking this post on DEV Community, and let me know on Twitter. I get lonely sometimes.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>11 podcasts for the frontend developer</title>
      <dc:creator>Ryan Boone</dc:creator>
      <pubDate>Sun, 21 Feb 2021 20:53:46 +0000</pubDate>
      <link>https://dev.to/falldowngoboone/11-podcasts-for-the-frontend-developer-3l5k</link>
      <guid>https://dev.to/falldowngoboone/11-podcasts-for-the-frontend-developer-3l5k</guid>
      <description>&lt;p&gt;I'm a huge fan of continuing education, and one of the ways I binge on information is through podcasts. Here's a list of 11 podcasts that I find not only educational but entertaining as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://changelog.com/jsparty"&gt;JS Party&lt;/a&gt; - From the folks at Changelog, this weekly, hour-ish podcast focusing on JavaScript features a rotating panel and format.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://frontendhappyhour.com"&gt;Front End Happy Hour&lt;/a&gt; - A bunch of developers drinking and talking about development. What could go wrong?&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spec.fm/podcasts/developer-tea"&gt;Developer Tea&lt;/a&gt; - This podcast is more about the human element of development. I recommend this to developers of all levels, but especially beginners.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fsjam.org"&gt;FSJam Podcast&lt;/a&gt; - Fullstack Jamstack conversations with developers, library maintainers, and industry thought leaders.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://99percentinvisible.org"&gt;99% Invisible&lt;/a&gt; - It's not all dev-centric podcasts here. &lt;em&gt;99% Invisible&lt;/em&gt; peels back the curtain on the design of everyday things. &lt;a href="https://99percentinvisible.org/episode/episode-06-99-symbolic/"&gt;The flag episode&lt;/a&gt; is a classic.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fullstackradio.com"&gt;Full Stack Radio&lt;/a&gt; - Adam Wathan, creator of (among other things) Tailwind, hosts this podcast with a focus on all the things happening on the front end.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hiddenbrain.org"&gt;Hidden Brain&lt;/a&gt; - This fascinating podcast delves into why we do the things we do and how we can learn from that. It's honestly one of my favs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://reactpodcast.com"&gt;React Podcast&lt;/a&gt; - It's about React. So...&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shoptalkshow.com"&gt;ShopTalk Show&lt;/a&gt; - Chris Coyier (&lt;a href="https://css-tricks.com"&gt;CSS-Tricks&lt;/a&gt;) and Dave Rupert (&lt;a href="https://paravelinc.com"&gt;Paravel&lt;/a&gt;) host this quirky podcast (mostly) about frontend. This podcast was my first developer podcast ever and still one of my favorites.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://syntax.fm"&gt;Syntax&lt;/a&gt; - Hosted by Wes Bos and Scott Tolinski, I highly recommend this podcast if you are new to JavaScript. It's chock-full of tips and sick picks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://freakonomics.com/archive/"&gt;Freakonomics Radio&lt;/a&gt; - Again, a podcast about humans for humans. This one has a strong focus on Behavioral Economics, the cross-section of psychology and econ.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't see your favorite here? &lt;a href="https://twitter.com/therealboone"&gt;Let me know on Twitter&lt;/a&gt;. And if you found this post helpful, please let me know by liking it on DEV Community.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>education</category>
    </item>
  </channel>
</rss>
