<?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: Westbrook Johnson</title>
    <description>The latest articles on DEV Community by Westbrook Johnson (@westbrook).</description>
    <link>https://dev.to/westbrook</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%2F82953%2F3c83b5f5-fd0e-4cdc-a681-d84b6953699b.jpeg</url>
      <title>DEV Community: Westbrook Johnson</title>
      <link>https://dev.to/westbrook</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/westbrook"/>
    <language>en</language>
    <item>
      <title>Testing with JS is like magic, but is it science?</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Sat, 16 Sep 2023 15:34:55 +0000</pubDate>
      <link>https://dev.to/westbrook/testing-with-js-is-like-magic-but-is-it-science-23f8</link>
      <guid>https://dev.to/westbrook/testing-with-js-is-like-magic-but-is-it-science-23f8</guid>
      <description>&lt;p&gt;If that title doesn't get you riled up, I know what will...&lt;/p&gt;

&lt;h2&gt;
  
  
  Last time on "Testing Web Components"
&lt;/h2&gt;

&lt;p&gt;That's right, a recap!&lt;/p&gt;

&lt;p&gt;If you haven't read the first installment of &lt;a href="https://dev.to/westbrook/testing-web-components-with-webtest-runner-51g6"&gt;Testing Web Components with @web/test-runner&lt;/a&gt;, go do it now... I'll wait... If you just did, or you had previously, here are some fun things to have at the top of your mind before digging into today's episode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you can quickly generate a new web component repo with &lt;a href="https://open-wc.org/guides/developing-components/getting-started/" rel="noopener noreferrer"&gt;Open-WC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;the generator has a whole lot of options, but specifically, we're cooking with "Testing (&lt;a href="https://modern-web.dev/docs/test-runner/overview/" rel="noopener noreferrer"&gt;web-test-runner&lt;/a&gt;)" and Typescript&lt;/li&gt;
&lt;li&gt;the web component generated thereby is powered by &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; and leverages many of its &lt;a href="https://lit.dev/docs/components/decorators/" rel="noopener noreferrer"&gt;decorators&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;by default, it tests a few things intrinsic to the generated element, but mostly you'll be replacing that functionality with your own&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...and that's where we come in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with JS
&lt;/h2&gt;

&lt;p&gt;I can hear you now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do you mean, all of my JS tests are in JS, what's so magic about it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To which I'd respond, with a voice of disbelief, "You're testing JS with JS, what &lt;em&gt;isn't&lt;/em&gt; magic about it?"&lt;/p&gt;

&lt;p&gt;Then I'd remember that if you lived in my head already and knew all the jokes, you'd likely not be reading by this point. But, you're here, so I'll point out that I'm not talking about "magic"; that amazing, powerful stuff of lore that saves the heroine in your favorite story. I'm talking about "magic"; the thing that isn't real. Yes, testing your JS with JS isn't real. Or, it isn't real for at least half of your consumer types, which &lt;em&gt;likely&lt;/em&gt; represent an overwhelmingly large percentage of your actual consumers. That is the consumers that actually visit the page or application that you are building.&lt;/p&gt;

&lt;p&gt;To understand what I mean, let's revisit one of the tests that we've been bequeathed by the &lt;a href="https://open-wc.org/guides/developing-components/getting-started/#generator" rel="noopener noreferrer"&gt;Open WC Generator&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;increases the counter on button click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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 this test, we confirm that "clicking" the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; within the shadow root of the &lt;code&gt;&amp;lt;testing-components&amp;gt;&lt;/code&gt; element causes the &lt;code&gt;counter&lt;/code&gt; property on our element to equal &lt;code&gt;6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is an extremely common approach to interacting with the UI at unit testing time with JS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interacting with the UI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click" rel="noopener noreferrer"&gt;&lt;code&gt;.click()&lt;/code&gt;&lt;/a&gt; is a powerful part of the web API. It allows a developer to "simulate" the &lt;code&gt;click&lt;/code&gt; segment of a pointer interaction (possibly the most common segment) from the JS context. In this way, it can be a great way to guarantee the developer API that you surface with code.&lt;/p&gt;

&lt;p&gt;However, it's not &lt;em&gt;real&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;How do I know it's not real? Well, when was the last time you saw a visitor to your site or application type &lt;code&gt;el.click()&lt;/code&gt; into the console of their browser's DevTools? You, me, and our developer friends at debug time don't count here!&lt;/p&gt;

&lt;p&gt;This is a version of what's called "mocking". Mocking can be an important part of unit testing as it allows you to isolate the unit under test. This means that you can ignore inputs, side effects, and other realities applied to your unit by other contexts when testing that unit. When testing UI interactions, whether and how to mock the human that will be interacting with your UI will always be important to work out.&lt;/p&gt;

&lt;p&gt;Some &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;testing libraries&lt;/a&gt; look to mitigate this by &lt;a href="https://github.com/testing-library/user-event/blob/main/src/pointer/index.ts#L67-L111" rel="noopener noreferrer"&gt;more expansively mocking the interaction&lt;/a&gt;. You should definitely spend some time deciding whether the work going on here is something you think your testing needs, but, in a quick overview, some of the things being handled here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;possibly moving the pointer&lt;/li&gt;
&lt;li&gt;deciding whether the pointer should be released before starting a new interaction&lt;/li&gt;
&lt;li&gt;pressing down the pointer on a new (or the last) position&lt;/li&gt;
&lt;li&gt;possibly releasing the pointer again&lt;/li&gt;
&lt;li&gt;managing interaction with the "carat", which I couldn't 100% tell you what is in this context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily, this complexity is then hidden under &lt;a href="https://github.com/testing-library/user-event/blob/main/src/convenience/click.ts#L4-L12" rel="noopener noreferrer"&gt;helper methods&lt;/a&gt; because no one will remember to do all this &lt;em&gt;every&lt;/em&gt; time they want to better mimic a &lt;code&gt;.click()&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is there something better?&lt;br&gt;
— exasperated unit tester&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, &lt;a href="https://modern-web.dev/docs/test-runner/overview/" rel="noopener noreferrer"&gt;&lt;code&gt;@web/test-runner&lt;/code&gt;&lt;/a&gt; offers a &lt;a href="https://modern-web.dev/docs/test-runner/commands/" rel="noopener noreferrer"&gt;Commands API&lt;/a&gt; that supports you making requests to the browser runner of your tests from the JS test context. At a high level, this means that you can ask the browser runner (e.g. Playwright, Selenium, etc.) that you leverage at test time to act on the test context as if it actually were a person interacting with the page. By doing so, you can trigger things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pointer interactions (hello, &lt;code&gt;.click()&lt;/code&gt;, but better!)&lt;/li&gt;
&lt;li&gt;keyboard interactions&lt;/li&gt;
&lt;li&gt;interact with &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; menus (their default UI is outside of the browsing context)&lt;/li&gt;
&lt;li&gt;resize the window&lt;/li&gt;
&lt;li&gt;and, more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a &lt;code&gt;.click()&lt;/code&gt;, this looks a bit like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendMouse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;move&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&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;Looks pretty awesome, right? Well, that's not what we're going to be diving into today, sorry. In the next couple of installments, we'll take deeper looks at a number of these commands, and how you can write your own!&lt;/p&gt;

&lt;p&gt;For today, we're going to dive into the developer API that your newly generated custom element surfaces, &lt;em&gt;and you're gonna like it&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interacting with the developer API
&lt;/h3&gt;

&lt;p&gt;As mentioned above, &lt;code&gt;.click()&lt;/code&gt; is one way to surface interactions on your element to your developer consumers. In that way, this test &lt;em&gt;could&lt;/em&gt; be 100% real, and actually magic (the amazing kind). In this context, however, it's not.&lt;/p&gt;

&lt;p&gt;The offending code in this case is &lt;code&gt;.shadowRoot!&lt;/code&gt;. While &lt;code&gt;.click()&lt;/code&gt; can be a powerful API for consuming developers, if you're testing such an API on an element that a developer shouldn't have access to (any in the shadow DOM), then it's not real.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Using shadow DOM&lt;/a&gt; on an element encapsulates the contents of that shadow root from selectors from the parent application or component. This is why we can't use &lt;code&gt;el.querySelector&lt;/code&gt; directly to access this button. Not being able to do so, means that a consuming developer will not be able to do so as well. So, while reaching through the &lt;code&gt;.shadowRoot!&lt;/code&gt; may be a way to complete the coverage play of a component's unit tests, it's not something that actually guarantees actual API that any of your consumers would actually leverage.&lt;/p&gt;

&lt;p&gt;With this in mind, our test development cycle might go one of a couple of different ways. You could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;say, "Output says the lines are covered, I'm going home", and you'd not be wrong.&lt;/li&gt;
&lt;li&gt;leverage the &lt;a href="https://modern-web.dev/docs/test-runner/commands/" rel="noopener noreferrer"&gt;commands&lt;/a&gt; from above and close over the API to external developers by only testing as a visitor and not as a developer consuming your component.&lt;/li&gt;
&lt;li&gt;or, start a refactor party and look at what would be needed to make this API available to a developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before you take the path of most resistance, ask yourself "As a developer would I want this capability to be surfaced in some way?". If the answer is "yes", then hopefully wanting it points you towards a path of &lt;em&gt;how&lt;/em&gt; you'd want it surfaced, but if not, keep reading for some thoughts on what that might mean.&lt;/p&gt;

&lt;h4&gt;
  
  
  The developer API
&lt;/h4&gt;

&lt;p&gt;Sometimes a developer API is already available on the surface of your custom element, you just have to hold it right. Let's revisit our test to see if that might be appropriate here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;increases the counter on button click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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;What does "increases the counter on button click" mean, again:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;we load the element&lt;/li&gt;
&lt;li&gt;we find and click the button&lt;/li&gt;
&lt;li&gt;the counter equals &lt;code&gt;6&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ignoring for the moment the lack of precision as to how much the counter is increased, or coverage of what the counter should be by default (that's technically addressed in a previous test in our generated code), the operative word here is that a button clicked increases the &lt;code&gt;counter&lt;/code&gt;. This test is 100% for end consumers of your element. What might it be called if we were testing these capabilities for a developer consumer?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;counter&lt;/code&gt; can be set?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter&lt;/code&gt; can be increased?&lt;/li&gt;
&lt;li&gt;element can be clicked?&lt;/li&gt;
&lt;li&gt;something else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at how we might build these tests and refactor our element to support them.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;counter&lt;/code&gt; can be set
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;`counter` can be set&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// This isn't really a test of the side effects of the counter being set, so wouldn't be my first choice.&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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 test confirms that not only is &lt;code&gt;counter&lt;/code&gt; a getable property as confirmed in 'has a default title "Hey there" and counter 5' and the original version of 'increases the counter on button click', but it is also settable. Being this property leverages the &lt;a href="https://lit.dev/docs/api/decorators/#property" rel="noopener noreferrer"&gt;&lt;code&gt;@property&lt;/code&gt; decorator&lt;/a&gt; from &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, I can tell you for sure that it does have a setter, though our tests certainly hadn't guaranteed that previously. So, we'd be adding robustness to the generated code if we chose to add something like this. I would say that testing a setter with its getter &lt;em&gt;might&lt;/em&gt; leave something to be desired. Once we get deep into them in subsequent articles, confirming that the new value of &lt;code&gt;counter&lt;/code&gt; is leveraged by the rendered DOM via &lt;a href="https://modern-web.dev/docs/test-runner/commands/#snapshots" rel="noopener noreferrer"&gt;snapshot testing&lt;/a&gt; (not my favorite, I'll share more about that later) or actually rendered to the page via &lt;a href="https://github.com/modernweb-dev/web/tree/master/packages/test-runner-visual-regression" rel="noopener noreferrer"&gt;visual regression testing&lt;/a&gt; (key for UI libraries) would be a valuable extension of the test here.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;code&gt;counter&lt;/code&gt; can be increased
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;`counter` can be increased&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// The API doesn't currently exist on the element but could be surface as a public method for developers and leveraged within the click callback.&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increaseCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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;"Increased" here is playing fast and loose with the context of the original test. "increases the counter", as the only spec for the element, implies (in a way that is played out in the definition of the custom element) that you can only &lt;em&gt;increase&lt;/em&gt; the value of &lt;code&gt;counter&lt;/code&gt;. If this were true, I might also suggest you cover over the setter of &lt;code&gt;counter&lt;/code&gt; to prevent developer consumers from &lt;em&gt;decreasing&lt;/em&gt; the value of &lt;code&gt;counter&lt;/code&gt;. For now, we'll just go with increasing being the only thing we can do.&lt;/p&gt;

&lt;p&gt;Based on this assumption, the right thing might be just to surface the internal &lt;code&gt;__increment()&lt;/code&gt; method in a public manner. For brevities sake, we could rename it &lt;code&gt;increaseCounter&lt;/code&gt;, as seen in the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-   __increment() {
&lt;/span&gt;&lt;span class="gi"&gt;+   increaseCounter() {
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we'd refactor the click handler to leverage the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-       &amp;lt;button @click=${this.__increment}&amp;gt;increment&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+       &amp;lt;button @click=${this.increaseCounter}&amp;gt;increment&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, then we'd be back to passing tests.&lt;/p&gt;

&lt;h5&gt;
  
  
  element can be clicked
&lt;/h5&gt;

&lt;p&gt;Rather than requiring a consuming developer to find a &lt;em&gt;specific&lt;/em&gt; element to click on, where you are sure that this was the only clickable functionality in your element, you might be better off by surfacing a custom &lt;code&gt;click()&lt;/code&gt; method directly on your custom element. This can be tested as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element can be clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// This API is also not currently available, but by abstracting the idea of what clicking the element does you may be setting down a path where it does many things.&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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 refactor to make this possible is only a few steps in this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add the &lt;code&gt;click()&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;on, just one step, I guess
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+   click() {
+     this.__increment();
+   }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ship it!&lt;/p&gt;

&lt;p&gt;This means that you are binding your custom element to a singular action at &lt;code&gt;click&lt;/code&gt; time, so be sure this is what is actually intended for the functionality that you are shipping, but, if so, you're on your way to version 1.0!&lt;/p&gt;

&lt;h5&gt;
  
  
  something else
&lt;/h5&gt;

&lt;p&gt;All of these, and more, could serve your developer consumers by ensuring the API contract that you surface to them at unit test time. Within each is a sea of nuance that can be navigated more appropriately with the deeper knowledge of your goals that only you can bring to the discussion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should your custom element support the &lt;a href="https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md" rel="noopener noreferrer"&gt;https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md&lt;/a&gt; as a path to developer interaction?&lt;/li&gt;
&lt;li&gt;Should a more proprietary state interface be surfaced? Web components and Lit pair quite nicely with popular projects like &lt;a href="https://github.com/adobe/lit-mobx" rel="noopener noreferrer"&gt;Mobx&lt;/a&gt;, &lt;a href="https://github.com/Polymer/pwa-helpers/blob/master/src/connect-mixin.ts" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;, &lt;a href="https://github.com/statelyai/xstate/pull/2581" rel="noopener noreferrer"&gt;XState&lt;/a&gt;, and on and on.&lt;/li&gt;
&lt;li&gt;Should you devise a purpose-built custom events API for orchestrating imperative cross-DOM interactions? (Probably not..but, you could.)&lt;/li&gt;
&lt;li&gt;Should a similar approach to &lt;code&gt;increaseCounter()&lt;/code&gt; above be leveraged against a more specific method name clarifying the internal reaction to the state change?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only you can answer these questions. However, what's great about code is that if you can imagine it then with the right amount of time you can code it, so go wild!&lt;/p&gt;

&lt;h2&gt;
  
  
  Being less "magic" and more "amazing"
&lt;/h2&gt;

&lt;p&gt;Focusing your approach to testing on how the actual consumers of your code will interact with that code is an important part of ensuring that the work you do is less magic (the not real kind). The less magic in your tests, the more predictable your work will become. Contrary to popular belief shipping simple, dependable, well-tested features is much more amazing for you consumers than something that could disappear right in front of them.&lt;/p&gt;

&lt;p&gt;Above we've learned about some refactoring strategies that our testing cycle can point us towards as we work to make our API contracts simpler, more dependable, and more likely to support our developer consumers to amazing results. Next time, we'll get back to our UI-first consumers and how we can better embody them at test time, as well. Specifically, we'll focus on the &lt;a href="https://modern-web.dev/docs/test-runner/commands/" rel="noopener noreferrer"&gt;Commands API from @web/test-runner&lt;/a&gt; and ways to more accurately mock pointer interactions with your code. See you then!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Enter, it screams... I mean, it streams</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Fri, 28 Jul 2023 14:31:12 +0000</pubDate>
      <link>https://dev.to/westbrook/enter-it-screams-i-mean-it-streams-2p5p</link>
      <guid>https://dev.to/westbrook/enter-it-screams-i-mean-it-streams-2p5p</guid>
      <description>&lt;p&gt;Have you ever thought to write an event listener like the following?&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;button&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;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;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;button&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, gotten reports that the actual work that you listed (please don't ship &lt;code&gt;console.log()&lt;/code&gt; statements to production) was happening way too often?&lt;/p&gt;

&lt;p&gt;Maybe you wrote your listener like this, and got similar reports?&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;button&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;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;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;button&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;keydown&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;code&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;If you've already caught the context, don't ruin the surprise...&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard-based interactions
&lt;/h2&gt;

&lt;p&gt;To mimic pointer interactions when navigating an application with the keyboard, when the &lt;code&gt;Space&lt;/code&gt; key is pressed and a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element is in focus, the &lt;code&gt;keyup&lt;/code&gt; event from that interaction is duplicated as a &lt;code&gt;click&lt;/code&gt; event. Similarly, the &lt;code&gt;keydown&lt;/code&gt; event (or &lt;code&gt;keypress&lt;/code&gt; event, if you're into deprecated APIs) on the &lt;code&gt;Enter&lt;/code&gt; key, in the same situation, will be duplicated as a &lt;code&gt;click&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;However, the &lt;code&gt;Enter&lt;/code&gt; key is special. The &lt;code&gt;Enter&lt;/code&gt; key screams...I mean, streams its &lt;code&gt;keydown&lt;/code&gt; event. That means as long as a visitor has their &lt;code&gt;Enter&lt;/code&gt; key down the &lt;code&gt;keydown&lt;/code&gt; event, which usually is tightly coupled to when the key &lt;em&gt;goes down&lt;/em&gt;, will continue to fire. This means that our &lt;code&gt;keydown&lt;/code&gt; listener AND the &lt;code&gt;click&lt;/code&gt; listener in the above code sample will stream as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  My own haunted house
&lt;/h2&gt;

&lt;p&gt;I was recently surprised by this reality when a listener like this that I have bound threw focus into a different element that also had a listener like this bound, which subsequently through focus back into the first element and created a loop 😱&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;button1&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;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;button.one&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;button2&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;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;button.two&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;button1&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;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="nx"&gt;button2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;button2&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;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="nx"&gt;button1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Normally&lt;/em&gt;, the event listeners worked exactly as planned, but then came the bumps in the night... luckily, I have great coworkers that helped catch this use case at code review time, before it went live to users. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter at your own risk
&lt;/h2&gt;

&lt;p&gt;Maybe yelling the event listener at your visitor is the right thing to do in the context of your application, but, if it is not, key this reality in mind to ensure that your application delivers the expected experience.&lt;/p&gt;

&lt;p&gt;If you're interested in coalescing the interaction with the &lt;code&gt;Enter&lt;/code&gt; key to a one-time event, you might be interested in the &lt;code&gt;keyup&lt;/code&gt; event:&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;button&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;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;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;button&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;keyup&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;code&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;If you're actually working with a &lt;code&gt;click&lt;/code&gt; event, you may want to do a more complex form of gating:&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;let&lt;/span&gt; &lt;span class="nx"&gt;enterKeydown&lt;/span&gt; &lt;span class="o"&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;const&lt;/span&gt; &lt;span class="nx"&gt;button&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;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;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;button&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;keydown&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;event&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;if &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;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enterKeydown&lt;/span&gt;&lt;span class="p"&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="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;enterKeydown&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="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;button&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;keyup&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;event&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;if &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;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&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;enterKeydown&lt;/span&gt; &lt;span class="o"&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;button&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, maybe you have an alternative way to not scare your visitors? If so, share it in the comments below! I'd love to hear what you've been doing to prevent the &lt;code&gt;Enter&lt;/code&gt; key from screaming at anyone who has to listen to your application.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>eventdriven</category>
      <category>programming</category>
    </item>
    <item>
      <title>Testing Web Components with @web/test-runner</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Thu, 16 Mar 2023 19:12:00 +0000</pubDate>
      <link>https://dev.to/westbrook/testing-web-components-with-webtest-runner-51g6</link>
      <guid>https://dev.to/westbrook/testing-web-components-with-webtest-runner-51g6</guid>
      <description>&lt;p&gt;So, you write web components and you're interested in expanding the work you put into unit testing them? Well, you've come to the right place. This is just the beginning, but &lt;em&gt;Testing Web Components: the Series&lt;/em&gt; is going to lay out for you how &lt;a href="https://open-wc.org/" rel="noopener noreferrer"&gt;Open Web Components&lt;/a&gt; and &lt;a href="https://modern-web.dev/" rel="noopener noreferrer"&gt;Modern Web&lt;/a&gt; help you to do just that. We'll start with how the Open Web Components &lt;a href="https://open-wc.org/guides/developing-components/getting-started/" rel="noopener noreferrer"&gt;generator&lt;/a&gt; can get you up and running in no time with &lt;a href="https://modern-web.dev/docs/test-runner/overview/" rel="noopener noreferrer"&gt;&lt;code&gt;@web/test-runner&lt;/code&gt;&lt;/a&gt; right out of the box.&lt;/p&gt;

&lt;p&gt;While the component and tests that come with the generator are a great place to get started, in future posts we'll dig even deeper into what it means to test your web components in real browsers. Along the way, we'll focus on several realities around unit testing web interfaces, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the capabilities (and limitations) of testing UI from the JS thread&lt;/li&gt;
&lt;li&gt;how you can test more like a user will consume your component, whether that's with a mouse, a keyboard, or a little slower the normal JS execution time&lt;/li&gt;
&lt;li&gt;what sort of accessibility guarantees you can test at unit time&lt;/li&gt;
&lt;li&gt;and more...&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before we get started
&lt;/h2&gt;

&lt;p&gt;To prepare, let's take a look at the component that comes with the Open Web Components generator and the tests that are already written for it. To get started run &lt;code&gt;npm init @open-wc@latest&lt;/code&gt; in your terminal. &lt;em&gt;Be aware that &lt;code&gt;npm init&lt;/code&gt; has a pretty sticky cache. If you've run the command before, the inclusion of &lt;code&gt;@latest&lt;/code&gt; will ensure you get the most current output.&lt;/em&gt; We'll be &lt;strong&gt;scaffolding a new project&lt;/strong&gt;, in which we will build a &lt;strong&gt;web component&lt;/strong&gt;, to which we'll want to add &lt;strong&gt;testing&lt;/strong&gt;, all of which we'll do in &lt;strong&gt;Typescript&lt;/strong&gt;, and will call our component &lt;code&gt;testing-components&lt;/code&gt;. If you give the generator permission to write this content to disk and install dependencies with NPM, the terminal output should look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init @open-wc@latest
Need to &lt;span class="nb"&gt;install &lt;/span&gt;the following packages:
  @open-wc/create@0.38.69
Ok to proceed? &lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="o"&gt;)&lt;/span&gt; y

        _.,,,,,,,,,._
     .d&lt;span class="s1"&gt;''&lt;/span&gt;           &lt;span class="sb"&gt;``&lt;/span&gt;b.       Open Web Components Recommendations
   .p&lt;span class="s1"&gt;'      Open       `q.
 .d'&lt;/span&gt;    Web Components  &lt;span class="sb"&gt;`&lt;/span&gt;b.    Start or upgrade your web component project with
 .d&lt;span class="s1"&gt;'                     `b.   ease. All our recommendations at your fingertips.
 ::   .................   ::
 `p.                     .q'&lt;/span&gt;
  &lt;span class="sb"&gt;`&lt;/span&gt;p.    open-wc.org    .q&lt;span class="s1"&gt;'
   `b.     @openWc     .d'&lt;/span&gt;
     &lt;span class="sb"&gt;`&lt;/span&gt;q..            ..,&lt;span class="s1"&gt;'      See more details at https://open-wc.org/init/
        '',,,,,,,,,,''


Note: you can exit any time with Ctrl+C or Esc
✔ What would you like to do today? › Scaffold a new project
✔ What would you like to scaffold? › Web Component
✔ What would you like to add? › Testing (web-test-runner)
✔ Would you like to use typescript? › Yes
✔ What is the tag name of your web component? … testing-components

./
├── testing-components/
│   ├── .vscode/
│   │   └── extensions.json
│   ├── demo/
│   │   └── index.html
│   ├── src/
│   │   ├── index.ts
│   │   ├── testing-components.ts
│   │   └── TestingComponents.ts
│   ├── test/
│   │   └── testing-components.test.ts
│   ├── .editorconfig
│   ├── .gitignore
│   ├── LICENSE
│   ├── package.json
│   ├── README.md
│   ├── tsconfig.json
│   ├── web-dev-server.config.mjs
│   └── web-test-runner.config.mjs

✔ Do you want to write this file structure to disk? › Yes
Writing..... done
✔ Do you want to install dependencies? › Yes, with npm
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you run &lt;code&gt;cd testing-components&lt;/code&gt; to get into the generated project, and open the directory in your editor of choice, we'll dive into what's been created for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generated project
&lt;/h2&gt;

&lt;p&gt;The first thing you'll notice are the choices from the Open Web Components generator that we didn't adopt; Linting (eslint &amp;amp; prettier) and Demoing (Storybook). As they aren't specifically germane to actually testing our web component, I've left them out of the repo as generated for this article, but that doesn't mean you have to do so. Whether you chose to include them now, or you choose to "Upgrade an existing project" later, these things won't specifically get in the way, but in a world where everything happens 90 miles a minute, it can be useful to start with some focus.&lt;/p&gt;

&lt;p&gt;Then, you'll see what we did include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;demo&lt;/code&gt; directory holds a very simple &lt;code&gt;index.html&lt;/code&gt; that is served by &lt;code&gt;@web/dev-server&lt;/code&gt; via the &lt;code&gt;npm start&lt;/code&gt; command, if you'd like to check out the web component that comes stock with the generator in action&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;dist&lt;/code&gt; directory holds the JS output from running &lt;code&gt;tsc&lt;/code&gt; against our Typescript source, this command is included before almost all other scripts you may choose to run, so you shouldn't ever need to do so directly&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;src&lt;/code&gt; directory holds our web component which is broken into an &lt;code&gt;index.js&lt;/code&gt; re-exporting the class definition exported from &lt;code&gt;TestingComponents.ts&lt;/code&gt; as well as &lt;code&gt;testing-components.ts&lt;/code&gt; that specifically registers that same class as the &lt;code&gt;testing-component&lt;/code&gt; custom element name&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;test&lt;/code&gt; directory with one test file, &lt;code&gt;testing-components.test.ts&lt;/code&gt;, that can be run via the &lt;code&gt;npm test&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't let me stop you, now. I know you want to see for yourself, "will it blend?". Give &lt;code&gt;npm start&lt;/code&gt; a run... you'll get something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmh6jutb3far9bofc752.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmh6jutb3far9bofc752.png" alt="The initial output of our &amp;lt;testing-components&amp;gt; element, saying " width="654" height="348"&gt;&lt;/a&gt;&lt;/p&gt;
The initial output of our `` element, saying "Hello owc World! Nr. 5!" with a button that says "Increment"



&lt;p&gt;You can also give the tests a blend with the &lt;code&gt;npm test&lt;/code&gt; command. If all things have gone as planned the output should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; testing-components@0.0.0 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tsc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wtr &lt;span class="nt"&gt;--coverage&lt;/span&gt;


dist/test/testing-components.test.js:

 🚧 Browser logs:
      Lit is &lt;span class="k"&gt;in &lt;/span&gt;dev mode. Not recommended &lt;span class="k"&gt;for &lt;/span&gt;production! See https://lit.dev/msg/dev-mode &lt;span class="k"&gt;for &lt;/span&gt;more information.

Chrome: |██████████████████████████████| 1/1 &lt;span class="nb"&gt;test &lt;/span&gt;files | 4 passed, 0 failed

Code coverage: 100 %
View full coverage report at coverage/lcov-report/index.html

Finished running tests &lt;span class="k"&gt;in &lt;/span&gt;9s, all tests passed! 🎉
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we've got a pretty minimal web component and four passing tests. Let's dig in a little further to see what have we gotten for our trouble and what is actually under test by default here.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;&amp;lt;testing-components&amp;gt;&lt;/code&gt; element
&lt;/h2&gt;

&lt;p&gt;There's not much to our &lt;code&gt;&amp;lt;testing-component&amp;gt;&lt;/code&gt; element, but here it is in all of its glory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;html&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;LitElement&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;lit&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;property&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;lit/decorators.js&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;class&lt;/span&gt; &lt;span class="nc"&gt;TestingComponents&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
    :host {
      display: block;
      padding: 25px;
      color: var(--testing-components-text-color, #000);
    }
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hey there&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;__increment&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;counter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;h2&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;header&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Nr. &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;counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/h2&amp;gt;
      &amp;lt;button @click=&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;__increment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;increment&amp;lt;/button&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated Typescript above heavily relies on &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; to keep your web component both fast and simple. Here are some important facts to take away from this code regarding the tests we'll investigate next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the class definition leverages the &lt;a href="https://lit.dev/docs/api/LitElement/" rel="noopener noreferrer"&gt;&lt;code&gt;LitElement&lt;/code&gt;&lt;/a&gt; base class, so check out &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;lit.dev&lt;/a&gt; for more information about what that means when we get to adding new functionality in later installments&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://lit.dev/docs/api/decorators/#property" rel="noopener noreferrer"&gt;&lt;code&gt;property&lt;/code&gt; decorator&lt;/a&gt; is being leveraged to upgrade a couple of properties to be both reactive and bound to attributes on the element&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://lit.dev/docs/templates/overview/" rel="noopener noreferrer"&gt;&lt;code&gt;html&lt;/code&gt; template literal&lt;/a&gt; from is being leveraged to performantly render out template&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://lit.dev/docs/components/events/#listening-to-events" rel="noopener noreferrer"&gt;&lt;code&gt;@&lt;/code&gt; sigil&lt;/a&gt; is being leveraged to declaratively listen to events on elements within our template&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The tests
&lt;/h2&gt;

&lt;p&gt;In our tests, a web component defined by our &lt;code&gt;TestingComponents&lt;/code&gt; class is ensured to have the following characteristics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it has a default header "Hey there" and counter 5&lt;/li&gt;
&lt;li&gt;it increases the counter on button clicks&lt;/li&gt;
&lt;li&gt;it can override the header via attribute&lt;/li&gt;
&lt;li&gt;it asses the a11y audit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Defaults, interactivity, customization, and accessibility, form a great baseline for testing most web UI, so let's look closer at how this is done for our &lt;code&gt;&amp;amp;lt;testing-components&amp;amp;gt;&lt;/code&gt; element, and what else might be useful in these areas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defaults
&lt;/h3&gt;

&lt;p&gt;As we saw above, the &lt;code&gt;@property&lt;/code&gt; decorator is being leveraged to create reactive properties on our class definition. &lt;code&gt;header&lt;/code&gt; is being decorated with &lt;code&gt;type: string&lt;/code&gt; meaning when its value is accepted from the attribute &lt;code&gt;header&lt;/code&gt; the value will be coerced to a string. Similarly, the &lt;code&gt;counter&lt;/code&gt; is decorated with &lt;code&gt;type: number&lt;/code&gt;, which means values applied to the &lt;code&gt;counter&lt;/code&gt; attribute will be coerced to a number. A handful of these conversions are handled out of the box by Lit, but in case you have something more complex in mind, learn about &lt;a href="https://lit.dev/docs/components/properties/#conversion-converter" rel="noopener noreferrer"&gt;custom converters&lt;/a&gt; to solve those use cases.&lt;/p&gt;

&lt;p&gt;Numbers and strings being pretty de rigueur, the following test confirms that those values are actually applied to the custom element when attached to the DOM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has a default header "Hey there" and counter 5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hey there&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing that a property equals its default value by default may feel a bit superfluous, but it ensures the baseline of your web component before you dig into anything else. Testing that a property equals a property may also look a little underwhelming, it doesn't actually test anything about the property, and you may be right. Depending on what such a property does in a more &lt;em&gt;complete&lt;/em&gt; web component, it may be advantageous to confirm that those properties appear appropriately in a &lt;a href="https://modern-web.dev/docs/test-runner/commands/#snapshots" rel="noopener noreferrer"&gt;DOM snapshot&lt;/a&gt; or in a &lt;a href="https://github.com/modernweb-dev/web/tree/master/packages/test-runner-visual-regression#test-runner-visual-regression" rel="noopener noreferrer"&gt;visual regression test&lt;/a&gt;. We'll get into these techniques, and more, in future installments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactivity
&lt;/h3&gt;

&lt;p&gt;When testing web UI it is important to think of your test inputs as a future visitor interacting with your web component or a future developer building with your web component. A visitor can interact with your code in many ways, including a &lt;a href="https://modern-web.dev/docs/test-runner/commands/#send-mouse" rel="noopener noreferrer"&gt;mouse&lt;/a&gt; and a &lt;a href="https://modern-web.dev/docs/test-runner/commands/#send-keys" rel="noopener noreferrer"&gt;keyboard&lt;/a&gt;, and &lt;code&gt;@web/test-runner&lt;/code&gt; offers some powerful ways to emulate those interactions that we will investigate, later. This generated test acts as a developer building with your web component; leveraging the fact that &lt;code&gt;@web/test-runner&lt;/code&gt; exists on the JS thread of an actual browser to leverage element APIs like &lt;code&gt;querySelector()&lt;/code&gt; and &lt;code&gt;click()&lt;/code&gt; within your test code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;increases the counter on button click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&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;If you're wondering why someone building with your web component might reach into your element and manually &lt;code&gt;click()&lt;/code&gt; something within your element, you're probably not the only one. However, at test time, doing this can sometimes be the only way to initiate interactions from the JS context. Other times, it can be a clear sign the API of your element hasn't fully considered the requirements of its public interface.&lt;/p&gt;

&lt;p&gt;Is clicking this button something that a consuming application should be able to do without human intervention? The answer may be "no", as is the case in the generated code, but, if the answer is "yes", you may want to refactor your element to prevent consuming developers from having to reach into your web component in this way. Understanding the various techniques of testing a web component can help surface these questions, and their answers before your code ships to consumers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customization
&lt;/h3&gt;

&lt;p&gt;By using the &lt;code&gt;property&lt;/code&gt; decorator in our class definition we not only upgraded those properties to be reactive, but we also bound those properties to attributes on our custom element. In this way, both &lt;code&gt;header&lt;/code&gt; and &lt;code&gt;counter&lt;/code&gt; can be supplied new values via the attributes of the same name. The following test ensures that this relationship is maintained for the &lt;code&gt;header&lt;/code&gt; attribute/property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;can override the header via attribute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components header="attribute header"&amp;gt;&amp;lt;/testing-components&amp;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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attribute header&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are one for being "complete", the same could be done for &lt;code&gt;counter&lt;/code&gt; as well. This test specifically leverages the &lt;code&gt;html&lt;/code&gt; template literal to create an instance of the &lt;code&gt;&amp;lt;testing-components&amp;gt;&lt;/code&gt; element and applies the value &lt;code&gt;attribute header&lt;/code&gt; as an attribute to that element. You could similarly apply that attribute manually by leveraging the element reference maintained in the &lt;code&gt;el&lt;/code&gt; variable; this would be the equivalent imperative operation &lt;code&gt;el.setAttribute('header', 'attribute header');&lt;/code&gt;, if you were one for being "complete"...&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility (a11y)
&lt;/h3&gt;

&lt;p&gt;Not only does the Open Web Components project provide the generator that we are basing our web component on in this conversation, but they also vend &lt;a href="https://open-wc.org/docs/testing/chai-a11y-axe/" rel="noopener noreferrer"&gt;Chai A11y aXe&lt;/a&gt; which binds &lt;a href="https://github.com/dequelabs/axe-core" rel="noopener noreferrer"&gt;&lt;code&gt;axe-core&lt;/code&gt;&lt;/a&gt; with &lt;a href="https://www.chaijs.com/" rel="noopener noreferrer"&gt;Chai&lt;/a&gt; assertions for easy consumption in the &lt;code&gt;@web/test-runner&lt;/code&gt; environment. This means that you can use the industry standard accessibility testing engine to confirm the accessibility of your web component at unit test time. This looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passes the a11y audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TestingComponents&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;testing-components&amp;gt;&amp;lt;/testing-components&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; &lt;code&gt;expect().to.be.accessible()&lt;/code&gt; is an asynchronous command. If you do not &lt;code&gt;await&lt;/code&gt; this expectation you will get false positives in your test results.&lt;/p&gt;

&lt;p&gt;But, when you do &lt;code&gt;await&lt;/code&gt; it, ensuring the snapshot accessibility of your custom element is just that simple. I saw "snapshot" here in that &lt;code&gt;.accessible()&lt;/code&gt; knows nothing about your web component other than the state from which you called it. That means if further interactions with the element change the accessibility model, you should be sure to add a test, or tests, that takes your web component into that state and confirms its accessibility in that context. In future articles, we'll take a close look at what that means, as well as the difference between a component possessing an accessible tree of DOM nodes and specifically delivering a unique accessibility tree to assistive technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  What have we got?
&lt;/h2&gt;

&lt;p&gt;Now you've got a pretty well-tested &lt;code&gt;&amp;lt;testing-components&amp;gt;&lt;/code&gt; element that features a &lt;code&gt;header&lt;/code&gt; and &lt;code&gt;counter&lt;/code&gt; attribute that delivers a button to your consumers that allows them to increment the counter. What more could you want?&lt;/p&gt;

&lt;p&gt;Oh... a lot?&lt;/p&gt;

&lt;p&gt;That seems fair. There isn't a lot in the component we've generated, which is good because it means that were you actually starting a new project with actual requirements there wouldn't be much to take away to start focusing on those. When we come back together in the future, we'll start from right here where we've left off and go deeper into testing web components in the browser with &lt;code&gt;@web/test-runner&lt;/code&gt;. Hopefully, you've got at least a rough handle on how things come together in this area and are excited to come along for the ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next...
&lt;/h2&gt;

&lt;p&gt;Now we know how to get started with a new web component project leveraging &lt;a href="https://open-wc.org/" rel="noopener noreferrer"&gt;Open Web Components&lt;/a&gt; &lt;a href="https://open-wc.org/guides/developing-components/getting-started/" rel="noopener noreferrer"&gt;generator&lt;/a&gt; and understand the basics of testing the defaults, interactivity, customization and accessibility of that element with &lt;a href="https://modern-web.dev/docs/test-runner/overview/" rel="noopener noreferrer"&gt;&lt;code&gt;@web/test-runner&lt;/code&gt;&lt;/a&gt;, but, in the long story of UI development and testing, we're just getting started. Throughout of future conversations, we'll be digging into more topics important to testing web components, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the capabilities (and limitations) of testing UI from the JS thread&lt;/li&gt;
&lt;li&gt;how you can test more like a user will consume your component, whether that's with a mouse, a keyboard, or a little slower the normal JS execution time&lt;/li&gt;
&lt;li&gt;what sort of accessibility guarantees you can test at unit time&lt;/li&gt;
&lt;li&gt;testing with DOM snapshots&lt;/li&gt;
&lt;li&gt;testing for visual regression&lt;/li&gt;
&lt;li&gt;and more...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've got a sharp eye, you'll notice the list is already getting longer as we go. Help me find things that I've missed. Take to the comments below with any concepts you would like to see covered in future installments. I can't guarantee that I'll be able to cover them, but now that you've gotten started testing web components, maybe you'll be able to teach me all about your favorite facet of the work.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Testing Accessibility with Shadow Roots</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Tue, 22 Feb 2022 13:16:49 +0000</pubDate>
      <link>https://dev.to/westbrook/testing-accessibility-with-shadow-roots-55cm</link>
      <guid>https://dev.to/westbrook/testing-accessibility-with-shadow-roots-55cm</guid>
      <description>&lt;p&gt;Recently, I had the opportunity to discuss the difficulties, learnings, and victories or developing &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt; together with fellow custom element developers from teams at &lt;a href="https://web-components.carbondesignsystem.com/?path=/story/introduction-welcome--page" rel="noopener noreferrer"&gt;IBM&lt;/a&gt;, &lt;a href="https://lion-web.netlify.app/" rel="noopener noreferrer"&gt;ING&lt;/a&gt;, &lt;a href="https://sap.github.io/ui5-webcomponents/" rel="noopener noreferrer"&gt;SAP&lt;/a&gt;, and &lt;a href="https://vaadin.com/components" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt;. If you missed the live stream, &lt;a href="https://www.youtube.com/watch?v=xz8yRVJMP2k&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;check out the recording&lt;/a&gt;! Fellow panelist, Ari Gilmore, made a great point that there is a lack of reading material for developers like ourselves to draw from when looking to build solid accessibility practices into the web components space. With that in mind, I thought it would be a good idea to take some of the abstract concepts we discussed in the panel and share some actual examples of working and testable code. Hopefully, this can better support the next developer(s) looking to bring a high-quality, accessible, design system to life for their team via web components.&lt;/p&gt;

&lt;p&gt;To support this conversation, I'll be bringing to life an input element pattern featuring accessible labeling and help text. Taking the suggestion of Thomas Allmer and the team at ING, the first example will be a no shadow DOM implementation with associated testing. With a shared baseline on how both the HTML and the testing works, we'll explore some different examples of delivering the relationship an input element, label element, and help text element accessibly with custom elements and shadow DOM. We'll talk about ways that we can mix and match these approaches and how some of the approaches align with or support various in-development and draft specifications for making this process even less work.&lt;/p&gt;

&lt;p&gt;Some particularly prescient subjects that we went over during the panel that I'll dive into in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;leveraging the &lt;a href="https://github.com/dequelabs/axe-core" rel="noopener noreferrer"&gt;axe-core&lt;/a&gt; accessibility testing engine&lt;/li&gt;
&lt;li&gt;seeing and understanding the accessibility tree&lt;/li&gt;
&lt;li&gt;using native keyboard interactions at testing time&lt;/li&gt;
&lt;li&gt;how ID references do not pass through a shadow boundary&lt;/li&gt;
&lt;li&gt;ways to mimic native element references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll also connect some dots between these techniques and the various web component libraries my fellow panelists have brought into the world that leverages them so you can follow up on what is needed to take those patterns into production.&lt;/p&gt;

&lt;p&gt;Some subjects that I won't spend much time on in this article, but are of great importance when shipping high quality, production-ready implementations of these patterns, include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;content styling&lt;/li&gt;
&lt;li&gt;form association&lt;/li&gt;
&lt;li&gt;state management&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these, and likely other topics omitted without reference, would easily fill their own article(s), and hopefully, the support you'll find here in getting a jump on making your shadow DOM-based content more accessible will free you up to share your approach to these realities, next.&lt;/p&gt;




&lt;h1&gt;
  
  
  Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Starting from HTML&lt;/li&gt;
&lt;li&gt;
What and how to test

&lt;ul&gt;
&lt;li&gt;axe-core&lt;/li&gt;
&lt;li&gt;Accessibility tree&lt;/li&gt;
&lt;li&gt;Native keyboard events at test time&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

How should we build it?

&lt;ul&gt;
&lt;li&gt;Factoring from raw HTML&lt;/li&gt;
&lt;li&gt;Wrapper&lt;/li&gt;
&lt;li&gt;Decorator&lt;/li&gt;
&lt;li&gt;
Emitter

&lt;ul&gt;
&lt;li&gt;The Decorator Pattern Plus&lt;/li&gt;
&lt;li&gt;Panelist projects leveraging this technique&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Outside-in

&lt;ul&gt;
&lt;li&gt;ID references DO NOT pass through shadow boundaries&lt;/li&gt;
&lt;li&gt;Panelist projects leveraging this technique&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Snowflakes

&lt;ul&gt;
&lt;li&gt;
Pretending to be a native element

&lt;ul&gt;
&lt;li&gt;Responding to the "for" attribute&lt;/li&gt;
&lt;li&gt;Observing text content&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Panelist projects leveraging this technique&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;In memoriam&lt;/li&gt;

&lt;li&gt;In the next life...&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
Before we get started, as I mentioned in the panel, I wouldn't call myself an accessibility specialist. I understand accessibility to be an important part of delivering products to people and strive for the tools I leverage to do so to be more and more accessible over time. In the community, I work with smart, caring people, like those that I joined on the panel, to find new and better ways to do things. At Adobe, while developing Spectrum Web Components, I work with a dedicated team of accessibility engineers, some of whom actually write the specs to which the entire web community develops software. Without their patience and support, I'd definitely not have gotten as far as I have in being able to bring accessible surfaces to the web. That certainly doesn't mean I get everything right. So, while I hope you find this article similarly useful, the only way for us all to get a little more accessible is for you to share in the comments in you find something I've missed, or a different way to achieve the same goals, or want to know something beyond what I'll be coving. We can all make the web a little better place, together!&lt;/p&gt;
&lt;h1&gt;
  
  
  Starting from HTML
&lt;/h1&gt;

&lt;p&gt;Following the lead of the ING team who's solid work has brought up &lt;a href="https://lion-web.netlify.app/" rel="noopener noreferrer"&gt;Lion&lt;/a&gt;, we'll start from the raw HTML pattern that delivers a labelled and described &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&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;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&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;"input"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/stories/index.stories.js?p=stories" rel="noopener noreferrer"&gt;view a demo of this code&lt;/a&gt; or &lt;a href="https://github.com/Westbrook/testing-a11y" rel="noopener noreferrer"&gt;clone it from GitHub&lt;/a&gt; to look more closely into how it works. However, the crux of the functionality (all provided natively by HTML at this point) is ID reference. &lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element accepts the &lt;code&gt;for&lt;/code&gt; attribute which references an element by ID. The element it references, in this case, our &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements, will be the one that receives the content of the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; as its "name" in the accessibility tree passed the screen reader. Locally, it will also pass the &lt;code&gt;click&lt;/code&gt; and &lt;code&gt;focus&lt;/code&gt; events triggered by clicking the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; onto the referenced element as well. This is less important in an &lt;code&gt;&amp;lt;input type="text"&amp;gt;&lt;/code&gt; element, however types like &lt;code&gt;checkbox&lt;/code&gt; or &lt;code&gt;radio&lt;/code&gt; will be toggled as appropriate when passing these events which can make both interactive with and styling your form content easier.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element in question is leveraging the &lt;code&gt;aria-describedby&lt;/code&gt; attribute, which also references an element by ID. Here, the attribute points to our &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element which holds our description. There is no default interactive functionality that this relationship provides, but it will supply the text content of the referenced element as the "description" of the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element in the accessibility tree.&lt;/p&gt;

&lt;h1&gt;
  
  
  What and how to test
&lt;/h1&gt;

&lt;p&gt;All of this is a great start to delivering this pattern accessibly, but don't just take my word for it. Let's dig into how we can test these things to be true, and at the same time set the table for refactoring this pattern to leverage web component APIs like custom elements and shadow DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  axe-core
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks, @open-wc/testing!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first stop in any accessibility testing (or possibly any UI testing) should be on tests you can get "for free". For our use case, that can be provided by the &lt;a href="https://github.com/dequelabs/axe-core" rel="noopener noreferrer"&gt;axe-core&lt;/a&gt; accessibility testing engine as packaged into &lt;a href="https://open-wc.org/docs/testing/chai-a11y-axe/" rel="noopener noreferrer"&gt;Chai a11y Axe&lt;/a&gt; and delivered in &lt;a href="https://www.npmjs.com/package/@open-wc/testing" rel="noopener noreferrer"&gt;&lt;code&gt;@open-wc/testing&lt;/code&gt;&lt;/a&gt;. While you may have caught me quoting a woefully small percentage of issues that automated testing like this can catch, I am heartened to hear that Deque (makers of axe-core) have recently looked deeper into this situation and believe that &lt;a href="https://accessibility.deque.com/hubfs/Accessibility-Coverage-Report.pdf" rel="noopener noreferrer"&gt;57.38% of accessibility issues can be discovered via automation&lt;/a&gt;, so this will be a big first step in affirming the accessibility of the patterns you deliver.&lt;/p&gt;

&lt;p&gt;What's more, this big first step is actually a really small step in reality. Check out the code below for confirming the accessibility of a DOM fixture with axe-core via Chai a11y axe:&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="c1"&gt;// Has the side effect of binding Chai A11y aXe to `expect`&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;fixture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&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;@open-wc/testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passes the aXe-core audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
&amp;lt;div&amp;gt;
    &amp;lt;label for="input"&amp;gt;Label&amp;lt;/label&amp;gt;
    &amp;lt;input id="input" aria-describedby="description" /&amp;gt;
    &amp;lt;div id="description"&amp;gt;Description&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Asynchronously tests the accessibility of the supplied content&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's right, the guts of the test at &lt;code&gt;await expect(el).to.be.accessible();&lt;/code&gt; and you'll immediately start getting reports of the accessibility achieved by the DOM in your fixture. Visit the &lt;a href="https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md" rel="noopener noreferrer"&gt;Rule Descriptions&lt;/a&gt; outlining all of the concepts that will be covered simply by adding the one test.&lt;/p&gt;

&lt;p&gt;This one test is so important that many tools get out in front of you to ensure it is included from the start. &lt;code&gt;npm init @open-wc&lt;/code&gt; will include this test by default when &lt;code&gt;testing&lt;/code&gt; is added by the generator. At Spectrum Web Components, when generating a new package with our &lt;a href="https://github.com/adobe/spectrum-web-components/blob/main/projects/templates/plop-templates/test.ts.hbs#L19-L29" rel="noopener noreferrer"&gt;Plop templating&lt;/a&gt;, we have this test by default as well. However you are initializing your projects, I highly suggest you work to have this sort of test included by default.&lt;/p&gt;

&lt;p&gt;With that out of the way, and as many as 57.38% of your accessibility issues already caught, we can take a look at some more nuanced contexts that can be useful to have covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility tree
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks, @web/test-runner!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The DOM tree, paired with various ID references and &lt;code&gt;aria-*&lt;/code&gt; attributes is used by the browser to construct an accessibility tree that it represented to screen readers to support visitors experiencing and interacting with our content with their assistance. Leveraging &lt;a href="https://www.w3.org/TR/wai-aria-practices-1.2/" rel="noopener noreferrer"&gt;WAI-ARIA Authoring Practices&lt;/a&gt;, along with the assurances that axe-core can set as a foundation, we can generally be sure of what the tree a browser might build from our content. However, it can beneficial to know for sure.&lt;/p&gt;

&lt;p&gt;One path to this is to leverage &lt;a href="https://developer.chrome.com/blog/full-accessibility-tree/" rel="noopener noreferrer"&gt;full accessibility tree&lt;/a&gt; view in Chrome DevTools. Switch this on and you can manually confirm the tree to which your code is being converted by the browser. Many browsers surface the ability to see &lt;em&gt;part&lt;/em&gt; of the accessibility tree via their developer tooling, which is very useful, but you often end up needing to rely on manual testing to confirm that the right content is being delivered to screen readers. &lt;/p&gt;

&lt;p&gt;While manual testing should definitely be a part of your go to production strategy, knowing what's actually in that tree, not just the relations that get created but the actual content that is bound by those relationships, can be an important addition to our automated testing workflows. To support this, browser runners like &lt;a href="https://playwright.dev/docs/api/class-accessibility" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; surface APIs by which we can access the accessibility tree (as a snapshot thereof) directly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@web/test-runner" rel="noopener noreferrer"&gt;&lt;code&gt;@web/test-runner&lt;/code&gt;&lt;/a&gt; give you access to these APIs while unit testing via its &lt;a href="https://modern-web.dev/docs/test-runner/commands/#accessibility-snapshot" rel="noopener noreferrer"&gt;commands&lt;/a&gt; interface. This allows you to snapshot the accessibility tree at any point during an interaction with your code at test time, and confirm that nodes and relations that you expect to exist are actually there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;a11ySnapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;findAccessibilityNode&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;@web/test-runner-commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Label&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;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`is labelled "&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="s2"&gt;" and described as "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
&amp;lt;div&amp;gt;
    &amp;lt;label for="input"&amp;gt;&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="s2"&gt;&amp;lt;/label&amp;gt;
    &amp;lt;input id="input" aria-describedby="description" /&amp;gt;
    &amp;lt;div id="description"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;a11ySnapshot&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;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;DescribedNode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DescribedNode&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;describedNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findAccessibilityNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;describedNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`node not in: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;  &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;null&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 above code actually builds our HTML implementation of the labeled and described input, appends it to the document, and then requests a snapshot of the accessibility tree. Then, using &lt;code&gt;findAccessibilityNode&lt;/code&gt; helper confirms the presence of a node meeting the requirements supplied. You'll also note that I leverage the custom error message of the Chai expectation here to allow for seeing the stringified accessibility tree when a test fails. As the tree can have many edge cases, and unexpected results, I've found this an important part of understanding what I'm testing.&lt;/p&gt;

&lt;p&gt;One way that the accessibility tree surprises me, in the case of this example, is that WebKit does not actively associate the description content with our &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element. While manual testing does confirm that the description text is associated appropriately, the tree will not return a reality where these two elements are connected. With the cross-context manual testing to back up this relationship in a WebKit browser, I'm comfortable with expanding the test in this case to something that takes this deviation into account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;findDescribedNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;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;await&lt;/span&gt; &lt;span class="nf"&gt;nextFrame&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;isWebKit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="sr"&gt;/AppleWebKit/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/Chrome/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&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;snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;a11ySnapshot&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;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;DescribedNode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DescribedNode&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// WebKit doesn't currently associate the `aria-describedby` element to the attribute&lt;/span&gt;
    &lt;span class="c1"&gt;// host in the accessibility tree. Give it an escape hatch for now.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;describedNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findAccessibilityNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isWebKit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;describedNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`node not in: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;  &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;null&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;isWebKit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Retest WebKit without the escape hatch, expecting it to fail.&lt;/span&gt;
        &lt;span class="c1"&gt;// This way we get notified when the results are as expected, again.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iOSNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findAccessibilityNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iOSNode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;null&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;You'll see that this test uses the user agent to compare for WebKit and then allows for the test to pass when the description isn't related to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and the browser is WebKit. To support understanding when/if this reality were to change in the future, the test then runs the expectation in reverse for WebKit so that a failure would be raised in our test and the workaround can be removed.&lt;/p&gt;

&lt;p&gt;In other testing scenarios, WebKit will associate description content without issue, so pay attention to your results and pair them with manual testing when setting the baselines from which you want to protect against regression. I've also seen these sorts of differences in cross-browser understandings of the &lt;code&gt;role&lt;/code&gt; of certain patterns, so be aware of the tree your content is creating when deciding the appropriate testing pattern to apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Native keyboard events at test time
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks, Playwright!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you are comfortable with the experience you are delivering to screen reader users, another user segment to ensure you are accessibly supporting keyboard navigation users. Whether to guide the screen reader across your content, or in support of other situations, it is important to know that your content can be accessed via the keyboard in expected ways. This can often prove elusive as testing keyboard events at unit test time is much more complex than it may seem. However, once you've established a reliable path to do so, the techniques leveraged to test this can be useful in other areas, as well; for instance, in UIs that include things like &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element, which would traditionally be interacted with via the keyboard by all users.&lt;/p&gt;

&lt;p&gt;Synthetic keyboard events can provide you with a decent entry into this area:&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;keyboardEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;eventDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nx"&gt;eventName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;KeyboardEvent&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;KeyboardEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&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;eventDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;code&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;Especially when the feature at test is also fully synthetic, e.g. something that you've &lt;em&gt;added&lt;/em&gt; to your input element, they can take you a long way. When you're looking to test more complex keyboard interactions, including the various phases on a keypress, you might even turn to a testing library or framework to manage the complexities therein with success.&lt;/p&gt;

&lt;p&gt;Once you move beyond testing your code directly, and into how your code should work in concert with the browser in which it is delivered synthetic events begin to show their shortcomings. This can be seen when attempting to use a synthetic event to alter the value of an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element. To fully mimic these interactions, more and more synthetic events need to be stacked on top of imperative commands to the to &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; until the whole process becomes quite brittle. Go one extra step and make expectations when a &lt;code&gt;Tab&lt;/code&gt; key is pressed, and the approach falls apart altogether.&lt;/p&gt;

&lt;p&gt;A native keyboard event doesn't just have all phases of a keypress that can be difficult to synthesize reliably, the browser itself recognizes the native keyboard interaction and responds with functionality beyond that which is found in your code. That means that the event has to originate with the browser itself. This is where tools like &lt;a href="https://playwright.dev/docs/api/class-keyboard" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; can step in and give you support for native keyboard interactions. Again, &lt;a href="https://www.npmjs.com/package/@web/test-runner" rel="noopener noreferrer"&gt;&lt;code&gt;@web/test-runner&lt;/code&gt;&lt;/a&gt; gives you access to these APIs while unit testing via its &lt;a href="https://modern-web.dev/docs/test-runner/commands/#send-keys" rel="noopener noreferrer"&gt;commands&lt;/a&gt; interface. Leveraging this allows us to place &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements before and after the code we have under test and ensure that &lt;code&gt;Tab&lt;/code&gt; and &lt;code&gt;Shift + Tab&lt;/code&gt; interactions behave as expected. Code for doing so might look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sendKeys&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;@web/test-runner-commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is part of the tab order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
&amp;lt;div&amp;gt;
    &amp;lt;label for="input"&amp;gt;&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="s2"&gt;&amp;lt;/label&amp;gt;
    &amp;lt;input id="input" aria-describedby="description" /&amp;gt;
    &amp;lt;div id="description"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&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;beforeInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;afterInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="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;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforebegin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afterend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;beforeInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&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;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;beforeInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`activeElement: &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;activeElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;press&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&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;expect&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;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`activeElement: &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;activeElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;press&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&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;expect&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;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;afterInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`activeElement: &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;activeElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;press&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Shift+Tab&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;expect&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;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`activeElement: &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;activeElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendKeys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;press&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Shift+Tab&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;expect&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;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;beforeInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`activeElement: &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;activeElement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;beforeInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;afterInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&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;You'll see here that our test consists of three &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements and applies focus to the first before using &lt;code&gt;Tab&lt;/code&gt; and &lt;code&gt;Shift + Tab&lt;/code&gt; keyboard events to navigate through them. This may feel like testing code that isn't yours, and you might be right in this case of all native &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements in the same DOM tree. However, when shadow DOM boundaries come into play, it becomes more important to confirm how a keyboard user might come into contact with the elements you are building. &lt;/p&gt;

&lt;h1&gt;
  
  
  How should we build it?
&lt;/h1&gt;

&lt;p&gt;There are a multitude of ways that we could structure this input experience with custom elements and shadow DOM. On top of each of those options is the ability to mix and match them across various contexts to make them work "just right" for your library or product. From here, let's dive into doing just that, while looking at some more "pure" implementations of the Wrapper, Decorator, Emitter, Outside-in, and Snowflakes techniques, as well as how the some of the panelist projects leverage them, or combinations thereof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factoring from raw HTML
&lt;/h2&gt;

&lt;p&gt;We've already seen the raw HTML that we'll be working from, but here it is again as a reminder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&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;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&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;"input"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/stories/index.stories.js?p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; of &lt;a href="https://webcomponents.dev" rel="noopener noreferrer"&gt;webcomponents.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Westbrook/testing-a11y" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub. &lt;/p&gt;

&lt;p&gt;Below, I've included five different ways to factor this raw HTML into custom elements, but they're just a small selection of the ways that you could do so accessibly. For each, we'll take a look at the custom elements that need to be created to leverage them, how those custom elements alter our usage in HTML, and what types of changes or additions might be needed to our test suite to support these decisions. I'll also link to examples of all or part of these techniques in work from my fellow panel members in the work on &lt;a href="https://web-components.carbondesignsystem.com/?path=/story/introduction-welcome--page" rel="noopener noreferrer"&gt;Carbon Web Components&lt;/a&gt;, &lt;a href="https://lion-web.netlify.app/" rel="noopener noreferrer"&gt;Lion&lt;/a&gt;, &lt;a href="https://sap.github.io/ui5-webcomponents/" rel="noopener noreferrer"&gt;SAP&lt;/a&gt;, and &lt;a href="https://vaadin.com/components" rel="noopener noreferrer"&gt;Vaadin&lt;/a&gt;, or in my own at &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapper
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/src/index.ts?branch=wrapper%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; on webcomponents.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/westbrook/testing-a11y/tree/wrapper" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;This techniques is called "wrapper" because really, all that we're doing is wrapping our previously accessibly HTML with a custom element:&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;testing-a11y&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;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&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;"input"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/testing-a11y&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, you're done. Ship it!&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element relies on the native accessibility of the raw HTML that we started with and then encapsulates the reusable functionality that you'd actually want to ship in a custom input element within the parent element wrapper. By itself, however, it places a lot of responsibility on a consuming developer to ensure that each usage fully completes the contract of accessibility promised by the raw HTML from which we started. I'd guess that this higher level requirement in their consumers lead other members on the panel not to use this technique as well, but you can always reach out to them and their teams for more information.&lt;/p&gt;

&lt;p&gt;In the case that you like the flexibility of this pattern, but prefer to lighten the burden on your consumers, check out our next pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decorator
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/src/index.ts?branch=decorator%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; on webcomponents.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/westbrook/testing-a11y/blob/decorator" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub.&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;testing-a11y&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Label&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="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/testing-a11y&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decorator pattern takes the wrapper pattern, and, like its name would suggest, decorates the provided HTML with the required attributes in order to deliver the pattern accessibly. When decorating HTML that is slotted into your custom element from the outside, it is important to remember that the owner of that code (the application or component above) may have expectations as to the state of that DOM with which it is best not to interfere. In that way, our &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element, in this case, will only apply the IDs needed to complete our accessibility contract when IDs are not already available on the element(s) in question. With the possibility, too, that any required aria attributes might already have associations applied to them, the element "conditions" those attributes into the ID reference list of those attributes rather than setting them to the decorated IDs only. This is a useful pattern in a number of contexts, and can be achieved with the following helper methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;conditionAttributeWithoutId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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;ariaDescribedby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&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="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;descriptors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ariaDescribedby&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ariaDescribedby&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&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;descriptors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;descriptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ids&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;descriptor&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;descriptors&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="nx"&gt;el&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="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;descriptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;el&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="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;conditionAttributeWithId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&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;ariaDescribedby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;el&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="nx"&gt;attribute&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;descriptors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ariaDescribedby&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ariaDescribedby&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&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;const&lt;/span&gt; &lt;span class="nx"&gt;hadIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;currentId&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;descriptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&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;hadIds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;noop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nx"&gt;descriptors&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;ids&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;el&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="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;descriptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &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;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;conditionAttributeWithoutId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ids&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;An element can &lt;code&gt;conditionAttributeWithId&lt;/code&gt; and then cache the returned &lt;code&gt;conditionAttributeWithoutId&lt;/code&gt; method to clean up at a later time, all without worrying about overwriting or removing values important to the parent context.&lt;/p&gt;

&lt;p&gt;Beyond that, this is a rather naive example of decorating DOM in this way and assumes the first &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; that is slotted into it is &lt;em&gt;the&lt;/em&gt; input should be decorating, and the same with the first &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element. Any other non-&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and non-&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element that it is provided is there to describe the input. However, it does nothing to ensure those are the only &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements that it receives or that it displays. Those elements would deliver content into the accessibility tree that is currently unmanaged, and any production-ready implementation of this pattern would benefit from additional validation to ensure that didn't happen. If this level of flexibility and the validation required to manage it seem uncomfortable, take a look a how our next pattern locks down the content our custom element can deliver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emitter
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/src/index.ts?branch=emitter%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; on webcomponents.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Westbrook/testing-a11y/tree/emitter" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub.&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;testing-a11y&lt;/span&gt;
    &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Label"&lt;/span&gt;
    &lt;span class="na"&gt;description=&lt;/span&gt;&lt;span class="s"&gt;"Description"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/testing-a11y&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turn the decorator pattern up to 11 and you end up with the emitter pattern. As you see in the HTML sample above, the consuming developer no longer has to structure any of their own HTML to be slotted into the &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element. The emitter pattern relied on attributes to supply the accessible content that it will deliver and then renders the accessible HTML from that data. This approach very closely resembles patterns you may have seen in the JSX contexts of other approaches to componentizing UI. The main difference is that the accessible HTML will be rendered &lt;em&gt;inside&lt;/em&gt; of the &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element as opposed to in the position marked by the call to a &lt;code&gt;&amp;lt;TestingA11y&amp;gt;&lt;/code&gt; function in JSX.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Decorator Pattern Plus
&lt;/h3&gt;

&lt;p&gt;At the intersection of the emitter pattern and the decorator pattern is the decorator pattern plus, which I have &lt;a href="https://medium.com/@westbrook/decorator-pattern-plus-816eefc89824" rel="noopener noreferrer"&gt;written about before&lt;/a&gt;. It's crazy to think that it's more than three years old now, but it still does a great job of introducing what would otherwise be a sixth pattern to cover for this article. Pair the concepts therein with the concepts above, both in regards to testing and relating &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements to label and description content and you might find the accessibility pattern for your next custom input element!&lt;/p&gt;

&lt;h3&gt;
  
  
  Panelist projects leveraging this technique
&lt;/h3&gt;

&lt;p&gt;&lt;a id="panelist-projects-leveraging-this-technique-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lion&lt;/strong&gt;&lt;br&gt;
The Lion library leverages a form of Decorator Pattern Plus in that it can either emit DOM based on the attributes or properties that it is provided or accept content for the various responsibilities slotted into its &lt;code&gt;&amp;lt;lion-input&amp;gt;&lt;/code&gt; element from the outside.&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;lion-input&lt;/span&gt;
    &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Label"&lt;/span&gt;
    &lt;span class="na"&gt;help-text=&lt;/span&gt;&lt;span class="s"&gt;"Description"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/lion-input&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;lion-input&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/lion-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is powered by their &lt;a href="https://github.com/ing-bank/lion/blob/master/packages/form-core/src/FormControlMixin.js" rel="noopener noreferrer"&gt;&lt;code&gt;FormControlMixin&lt;/code&gt;&lt;/a&gt; that makes the decoration or emission of light DOM content nice and uniform across their library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vaadin Web Components&lt;/strong&gt;&lt;br&gt;
Having mentioned as part of the panel that Lion had a lot of influence on their library, I'm unsurprised to see the Vaadin team also leveraging a form of Decorator Pattern Plus as well. Here, too, you can create a &lt;code&gt;&amp;lt;vaadin-text-field&amp;gt;&lt;/code&gt; from attributes/properties or slotted content.&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;vaadin-text-field&lt;/span&gt;
    &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Label"&lt;/span&gt;
    &lt;span class="na"&gt;helper-text=&lt;/span&gt;&lt;span class="s"&gt;"Description"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/vaadin-text-field&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;vaadin-text-field&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"helper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/vaadin-text-field&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here Vaadin leverages the &lt;a href="https://lit.dev/docs/composition/controllers/" rel="noopener noreferrer"&gt;reactive controller pattern&lt;/a&gt; popularized by the &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit team&lt;/a&gt; to manager various parts of this pattern. &lt;a href="https://github.com/vaadin/web-components/blob/master/packages/field-base/src/labelled-input-controller.js" rel="noopener noreferrer"&gt;Label content&lt;/a&gt;, &lt;a href="https://github.com/vaadin/web-components/blob/master/packages/field-base/src/field-aria-controller.js" rel="noopener noreferrer"&gt;description content&lt;/a&gt;, as well as the &lt;a href="https://github.com/vaadin/web-components/blob/master/packages/field-base/src/input-controller.js" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element&lt;/a&gt; itself are each managed in a way that is easily sharable across the library.&lt;/p&gt;

&lt;p&gt;In both of these cases, you are also given the option to choose what things you want to supply and where, while still having them bound to the accessibility tree correctly. This can lend a nice level of freedom to developer consuming your custom form elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outside-in
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/src/index.ts?branch=outside-in%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; on webcomponents.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Westbrook/testing-a11y/tree/outside-in" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub.&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;testing-a11y&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/testing-a11y&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this approach, there is content important to the accessibility story of the element on both the outside and the inside of our &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element. Inside, by default, consumers of this element are provided an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element, and from the outside content is addressed to &lt;code&gt;label&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; slots for their content to be associated with said &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; appropriately. Much like we saw with the emitter approach above, this allows a consuming developer to focus directly on providing the content they would like to deliver while the &lt;code&gt;&amp;lt;testing-a11y&amp;gt;&lt;/code&gt; element manages all of the accessible relations. This pattern goes one step further in not altering DOM contexts that it does not own, which can ensure eager rendering technologies employed at the parent application or element level will not interfere with the UI our custom element delivers.&lt;/p&gt;

&lt;h3&gt;
  
  
  ID references DO NOT pass through shadow boundaries
&lt;/h3&gt;

&lt;p&gt;This is the first technique we've looked at together where there is content important to delivering the accessibility of the pattern separated by shadow boundaries. In association with that, you'll notice that we are no longer supplying IDs directly on the element containing the label and description text content. This is because the ID reference created by the &lt;code&gt;for&lt;/code&gt; attribute on a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element and the &lt;code&gt;aria-describedby&lt;/code&gt; attribute on an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; DO NOT pass through shadow boundaries. To avoid this reality, we've wrapped the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements onto which we are projecting this content from the light DOM into our shadow DOM in elements that hold these references. Content projected into a custom element in this way will be attributed to those wrapping elements when the browser constructs the accessibility tree from this DOM to pass to the screen reader clearly delivering the content of this UI to the users they support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Panelist projects leveraging this technique
&lt;/h3&gt;

&lt;p&gt;&lt;a id="panelist-projects-leveraging-this-technique-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Carbon Web Components&lt;/strong&gt;&lt;br&gt;
We can see a full investment into the outside-in pattern in Carbon Web Components' &lt;a href="https://github.com/carbon-design-system/carbon-web-components/blob/main/src/components/input/input.ts" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;bx-input&amp;gt;&lt;/code&gt; element&lt;/a&gt;, including some additional slots for content beyond that covered herein.&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;bx-input&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"label-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"helper-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/bx-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the &lt;code&gt;&amp;lt;bx-input&amp;gt;&lt;/code&gt; to fully leverage the accessibility relationship created by the outside-in pattern for the &lt;a href="https://github.com/carbon-design-system/carbon-web-components/blob/aaaa893f15d1ab1dc08843fdf1261f02f249d6dd/src/components/input/input.ts#L212-L214" rel="noopener noreferrer"&gt;&lt;code&gt;label-text&lt;/code&gt; content&lt;/a&gt;. However, when taking a closer look, you'll see that the &lt;a href="https://github.com/carbon-design-system/carbon-web-components/blob/aaaa893f15d1ab1dc08843fdf1261f02f249d6dd/src/components/input/input.ts#L233-L238" rel="noopener noreferrer"&gt;&lt;code&gt;helper-text&lt;/code&gt; and &lt;code&gt;validity-message&lt;/code&gt; content&lt;/a&gt; is not currently associated with the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spectrum Web Components&lt;/strong&gt;&lt;br&gt;
In order to attach description content to form elements, including the &lt;code&gt;&amp;lt;sp-textfield&amp;gt;&lt;/code&gt; element in the Spectrum Web Components library, a &lt;code&gt;help-text&lt;/code&gt; slot is surfaced.&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;sp-textfield&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sp-textfield&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leverages the pattern outlined above very closely and expands to with a technique called &lt;a href="https://dev.to/westbrook/who-doesnt-love-some-s-3de0"&gt;Stacked Slots&lt;/a&gt; that allows you to easily manage multiple pieces of description content based on the validity of the &lt;code&gt;&amp;lt;sp-textfield&amp;gt;&lt;/code&gt; element. Even after all this time, I find that the patterns made available around slotted content and ensuring the accessibility of content leveraging shadow roots has much exploration to be had!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI5 Web Components&lt;/strong&gt;&lt;br&gt;
In conjunction with a visual design decision that delivers extra content about the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element in a "popover", the &lt;code&gt;&amp;lt;ui5-input&amp;gt;&lt;/code&gt; element from UI5 Web Components leverages a &lt;code&gt;valueStateMessage&lt;/code&gt; slot similar to this pattern. &lt;em&gt;Notice that the &lt;code&gt;value-state&lt;/code&gt; attribute must be set for content supplied in this manner to be displayed. This attribute accepts &lt;code&gt;Error&lt;/code&gt;, &lt;code&gt;Information&lt;/code&gt;, and &lt;code&gt;Warning&lt;/code&gt; in order to display this content at various visual severity levels.&lt;/em&gt;&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;ui5-input&lt;/span&gt; &lt;span class="na"&gt;value-state=&lt;/span&gt;&lt;span class="s"&gt;"Information"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"valueStateMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ui5-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, to achieve the delivery of the content via a popover, there is some additional machinery that goes into this implementation. By default, the text content applied to the &lt;code&gt;valueStateMessage&lt;/code&gt; text is &lt;a href="https://github.com/SAP/ui5-webcomponents/blob/8822f66438d1bea075866a70445b42487a347f09/packages/main/src/Input.js#L1302-L1312" rel="noopener noreferrer"&gt;duplicated into the shadow root&lt;/a&gt; of the &lt;code&gt;&amp;lt;ui5-input&amp;gt;&lt;/code&gt; element and associated to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; via a &lt;a href="https://github.com/SAP/ui5-webcomponents/blob/8822f66438d1bea075866a70445b42487a347f09/packages/main/src/Input.js#L1276" rel="noopener noreferrer"&gt;computed &lt;code&gt;aria-describedby&lt;/code&gt; attribute&lt;/a&gt; for screen readers. When the &lt;code&gt;&amp;lt;ui5-input&amp;gt;&lt;/code&gt; element is focused, any content supplied to the &lt;code&gt;valueStateMessage&lt;/code&gt; slot will then be copied just in time into a popover for visual delivery. &lt;/p&gt;

&lt;h2&gt;
  
  
  Snowflakes
&lt;/h2&gt;

&lt;p&gt;See the &lt;a href="https://webcomponents.dev/edit/9XdUWIy1wjJqyLFyii0p/src/index.ts?branch=snowflakes%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;demo&lt;/a&gt; on webcomponents.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Westbrook/testing-a11y/tree/snowflakes" rel="noopener noreferrer"&gt;Clone the code&lt;/a&gt; on GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;testing-a11y-label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/testing-a11y-label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;testing-a11y-input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/testing-a11y-input&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;testing-a11y-help-text&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="nt"&gt;&amp;lt;/testing-a11y-help-text&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everyone wants to be different, everyone wants to be unique, and sometimes custom elements feel this same way, too. To support them in this endeavor, here we outline what it might look like to make a fully custom implementation of each of the elements found in the raw HTML.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;testing-a11y-label&amp;gt;&lt;/code&gt; replaces the native &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element and take on the responsibility of both the focus forwarding that we confirmed in our test code, but also features its own &lt;code&gt;for&lt;/code&gt; attribute that must be powered by custom JS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;testing-a11y-input&amp;gt;&lt;/code&gt; replaces the native &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element and adds some simplicity by not requiring the &lt;code&gt;aria-describedby&lt;/code&gt; attribute any longer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;testing-a11y-help-text&amp;gt;&lt;/code&gt; helps clarify the anonymous nature of the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; we had previously leveraged for this content and also features its own &lt;code&gt;for&lt;/code&gt; attribute. We'll investigate how the lack of an &lt;code&gt;aria-description&lt;/code&gt; attribute in the platform makes managing this &lt;code&gt;for&lt;/code&gt; attribute different than the one on our &lt;code&gt;&amp;lt;testing-a11y-label&amp;gt;&lt;/code&gt; element below.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pretending to be a native element
&lt;/h3&gt;

&lt;p&gt;One of the key characteristics of the snowflake pattern is that you are making custom elements that mimic the native behavior of existing HTML elements rather than leveraging them directly, decorating them, or extending (not likely ever possibly without polyfilling in Safari) then. This means you'll need to be conscious of the capabilities you were otherwise getting "for free" in those native elements. One should be apparent by the use of the &lt;code&gt;for&lt;/code&gt; attribute in our custom label and help text elements above. Both &lt;code&gt;&amp;lt;testing-a11y-label&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;testing-a11y-help-text&amp;gt;&lt;/code&gt; will need to duplicate the ID reference established in native &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; elements by this attribute. In this pattern, the &lt;code&gt;for&lt;/code&gt; attribute points to an element that could be an actual form field, and you'll see code to support that possibility, but knowing that our &lt;code&gt;&amp;lt;testing-a11y-input&amp;gt;&lt;/code&gt; encapsulated its form element within its shadow DOM, we'll also need to prepare a path to keep the content relationship between two elements separated by a shadow boundary live.&lt;/p&gt;

&lt;h4&gt;
  
  
  Responding to the "for" attribute
&lt;/h4&gt;

&lt;p&gt;Part of the power that custom elements surface for developers in the lifecycle methods with which they can hook into browser native changes in our elements. Two of these are &lt;code&gt;observedAttributes&lt;/code&gt; and &lt;code&gt;attributeChangedCallback&lt;/code&gt; which allow us to &lt;a href="https://developers.google.com/web/fundamentals/web-components/customelements#attrchanges" rel="noopener noreferrer"&gt;observe attribute changed&lt;/a&gt;. With them we can easily react to changes in the &lt;code&gt;for&lt;/code&gt; attribute on our custom label and help text elements to ensure that those elements are appropriately associated to the element referenced thereby. Take a closer look at how we do that in &lt;code&gt;&amp;lt;testing-a11y-label&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;resolveForElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// House keeping for when the value of `for` changes from one ID to another.&lt;/span&gt;
    &lt;span class="k"&gt;if &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;conditionLabel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conditionLabel&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conditionLabelledby&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;conditionLabelledby&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="k"&gt;this&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&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;forElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// [1] Resolution of the element referenced by the ID provided as `for`. This resolution happens in the DOM tree in which the `&amp;lt;testing-a11y-label&amp;gt;` element exists, so the referenced element will need to exist there as well.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parent&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="nf"&gt;getRootNode&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;HTMLElement&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;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&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="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;LitElement&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;focusElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&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;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;whenDefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localName&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateComplete&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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;await&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateComplete&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// [2] Noralization of the referenced element as the referenced host or an element available via the `focusElement` property on that host (for cross shadow boundary referencing).&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;forElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focusElement&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;target&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forElement&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;targetParent&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;forElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRootNode&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;HTMLElement&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;targetParent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// [3a] Application of `aria-labelledby` for elements in the same DOM tree.&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;conditionLabelledby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;conditionAttributeWithId&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;forElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-labelledby&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;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// [3b] Application of `aria-label` for elements separated by shadow boundaries.&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;forElement&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="s1"&gt;aria-label&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;labelText&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;conditionLabel&lt;/span&gt; &lt;span class="o"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forElement&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="s1"&gt;aria-label&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;Looking specifically at the numbered comments above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For performance reasons this code requires that referenced elements live in the same DOM tree. If based on the requirements of your application this might be something you could relax. As seen in 5b, there is already support for associating content across shadow boundaries, so whether you step up through the various tree to the &lt;code&gt;document&lt;/code&gt; or choose to resolve the &lt;code&gt;forElement&lt;/code&gt; via alternate means (possibly accepting an actual element reference in the JS scope) you should be fully prepared to label that element with this code.&lt;/li&gt;
&lt;li&gt;The choice to resolve &lt;code&gt;target.focusElement || target&lt;/code&gt; for the &lt;code&gt;forElement&lt;/code&gt; likely restricts this approach to native form elements and custom form elements that have bought into this technique, which could be seen as unfortunate. However, it does closely mimic the &lt;a href="https://leobalter.github.io/cross-root-aria-delegation/" rel="noopener noreferrer"&gt;Cross-root Aria Delegation&lt;/a&gt; specification that is currently under development and widely agreed to across the various browser vendors.&lt;/li&gt;
&lt;li&gt;Here we gate between supporting elements in the same DOM tree and those separated by shadow boundaries. This ensures that our accessibility story continues to be delivered on even though native ID references are not able to bridge this divide themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When attempting to apply this same pattern to &lt;code&gt;&amp;lt;testing-a11y-help-text&amp;gt;&lt;/code&gt; you'll quickly discover that there is not &lt;code&gt;aria-description&lt;/code&gt; attribute. Because of this, associating our help text across shadow boundaries is a little more complex:&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;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complex-non-reusable-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hidden&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;proxy&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;labelText&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;forElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAdjacentElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afterend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;proxy&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;conditionDescribedby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;conditionAttributeWithId&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;forElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complex-non-reusable-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conditionDescribedby&lt;/span&gt; &lt;span class="o"&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="nx"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;conditionDescribedby&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 create a proxy element to inject into the DOM tree on the other side of the shadow boundary with which to associate the help text provided to our form element. This does mean that our &lt;code&gt;&amp;lt;testing-a11y-help-text&amp;gt;&lt;/code&gt; will be injecting DOM into a rendering scope that is does not own and it is important to keep in mind the limitations and dangers of doing so when choosing your path forward in this area. However, even when separated by this shadow boundary if you (or the same developer/library) own the elements on both sides of the divide these realities can be easily handled and the accessibility tree shaped from your content should be both stable and reliable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Observing text content
&lt;/h4&gt;

&lt;p&gt;When reaching across shadow boundaries to manage these label or description relationships, one added responsibility is insuring that the text content applied to the form element across the shadow boundary is kept up-to-date. Our elements will meed to observer their mutations in order to do this.&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="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="nf"&gt;connectedCallback&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;observer&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveForElement&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;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;characterData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;childList&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="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&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="nf"&gt;disconnectedCallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config of &lt;code&gt;{ characterData: true, subtree: true, childList: true }&lt;/code&gt; ensures that the observer will trigger on all changes to the value of &lt;code&gt;el.textContent&lt;/code&gt;. When that content changes it needs to be pushed over the shadow boundary into the other DOM tree so that the accessibility tree can be built with the expected relationships.&lt;/p&gt;

&lt;h3&gt;
  
  
  Panelist projects leveraging this technique
&lt;/h3&gt;

&lt;p&gt;&lt;a id="panelist-projects-leveraging-this-technique-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spectrum Web Components&lt;/strong&gt;&lt;br&gt;
This pattern is leveraged specifically for the &lt;code&gt;&amp;lt;sp-field-label&amp;gt;&lt;/code&gt; element in Spectrum Web Components to deliver label content to &lt;code&gt;&amp;lt;sp-textfield&amp;gt;&lt;/code&gt; elements when finishing the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; interface we've explored herein.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;sp-field-label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/sp-field-label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;sp-textfield&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/sp-textfield&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Spectrum Web Components library the &lt;code&gt;&amp;lt;sp-field-label&amp;gt;&lt;/code&gt; element uses &lt;a href="https://github.com/adobe/spectrum-web-components/blob/85e525827a8aabcb4ebf441f05b7e1789b590b8b/packages/field-label/src/FieldLabel.ts#L82-L107" rel="noopener noreferrer"&gt;almost line for line&lt;/a&gt; the approaches outlined above so that it can be leveraged in partnership with other native form elements or custom form element surfacing a &lt;code&gt;focusElement&lt;/code&gt; property to provide intentional access to specific children in the elements shadow DOM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI5 Web Components&lt;/strong&gt;&lt;br&gt;
Similarly, the &lt;code&gt;&amp;lt;ui5-label&amp;gt;&lt;/code&gt; element in UI5 Web Components also leverages this technique to a degree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ui5-label&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Label&lt;span class="nt"&gt;&amp;lt;/ui5-label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ui5-input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="na"&gt;accessible-name-ref=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ui5-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here they've baked &lt;code&gt;for&lt;/code&gt; attribute management into the &lt;a href="https://github.com/SAP/ui5-webcomponents/blob/16246a8ec08f4f4f287e4dcac53266da6d4bc60d/packages/main/src/Label.js#L142-L147" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;ui5-label&amp;gt;&lt;/code&gt; element&lt;/a&gt; and resolution from the form element to the label element via the &lt;code&gt;accessible-name-ref&lt;/code&gt; attribute as part of their &lt;a href="https://github.com/SAP/ui5-webcomponents/blob/master/packages/base/src/util/AriaLabelHelper.js" rel="noopener noreferrer"&gt;AriaLabelHelper utility&lt;/a&gt;. Yet another example as to how we're really only scratching the surface as to how you could ship accessible UI with shadow roots by looking at the handful of techniques included in this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  In memoriam
&lt;/h1&gt;

&lt;p&gt;If you hitherto thought that it was not possible to make shadow DOM accessible, thanks for sticking around this far to have that misunderstanding cleared up.&lt;/p&gt;

&lt;p&gt;If you understood how shadow DOM could be accessible, thanks for sticking around this far to hear about ways that I understand to do so.&lt;/p&gt;

&lt;p&gt;Hopefully everyone has a couple of extra tools for making their next web component, or one they're already shipping, even more accessible. But, remember, this is just a couple of the possibilities! If you've got other techniques that you know, use, or love, please, please share them in the comments so that whether it's me, or the next developer, the community at large can put them in their tool belt as well. &lt;/p&gt;

&lt;p&gt;Or even better, if you see something I missed, something I did wrong, or something you're want to know more about, get that conversation going. The only way we can all get better at delivering accessible UIs is by making it central to the conversation of delivering UIs. Sharing what we know, asking questions about what we don't, and pushing the envelope in every direction is a big part of that. I look forward to seeing it below!&lt;/p&gt;

&lt;h1&gt;
  
  
  In the next life...
&lt;/h1&gt;

&lt;p&gt;Like I mentioned at the start, there are MANY concepts around shipping a complete web component that we have not covered in this conversation. Including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;content styling&lt;/li&gt;
&lt;li&gt;form association&lt;/li&gt;
&lt;li&gt;state management&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each and every one of these topics and more would make a great follow up article expanding on these above patterns in any one of those directions. I can't make any promises, but I'll do my best...if you're interested in it, I'd love to partner, support, cheer you on as it comes together!&lt;/p&gt;

&lt;p&gt;On top of that, there are some more varied and complex patterns for accessibility with web component that could be useful to dig into, as well. In particular, the &lt;a href="https://www.youtube.com/watch?v=xz8yRVJMP2k&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;Vaadin panel&lt;/a&gt; touched on the super useful combobox patterns currently begin shipped by various products from other panelists which is currently raising up the backlog of &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt;. Sharing thoughts on how to bring that experience to the web with custom elements and shadow DOM might be just the nudge needed to actually get development of that pattern finished.&lt;/p&gt;

&lt;p&gt;Let's keep talking about accessibility. Let's keep making our UIs and components accessible. Let's find new and better patterns for doing it, together. See you next time.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>a11y</category>
      <category>shadowdom</category>
      <category>customelements</category>
    </item>
    <item>
      <title>Who doesn't love some `&lt;slot /&gt;`s?</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Wed, 24 Nov 2021 01:59:07 +0000</pubDate>
      <link>https://dev.to/westbrook/who-doesnt-love-some-s-3de0</link>
      <guid>https://dev.to/westbrook/who-doesnt-love-some-s-3de0</guid>
      <description>&lt;p&gt;It does seem like I enjoy a good &lt;code&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/code&gt;. I mean, look, I wrote about them all the way back in 2018 in &lt;a href="https://medium.com/@westbrook/slot-ing-in-some-tips-4f2763fc2ca" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;slot/&amp;gt;&lt;/code&gt;ing in Some Tips&lt;/a&gt;, and then in 2020, I spoke about &lt;a href="https://youtu.be/MS7y2K7tZto?t=3060" rel="noopener noreferrer"&gt;Stacked Slots&lt;/a&gt; at a virtual Web Components SF meetup (see the &lt;a href="https://webcomponents.dev/edit/5sXaRWYCLldZnVq9VjzT/src/code-example.ts" rel="noopener noreferrer"&gt;associated slides&lt;/a&gt;), before sharing a proof of concept for &lt;a href="https://webcomponents.dev/edit/F4jBbQpeMSujg9FtRrtZ/src/light-dom-as-model.ts" rel="noopener noreferrer"&gt;Light DOM as Model&lt;/a&gt;. And, as if that weren't enough, here we are again, and I'm writing to you, friend, about &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s. Today, we're going to get out of the theoretical and into the practical as we start on the path towards actual usage of Stacked Slots that I'm excited to bring to life as part of Adobe's &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt; to support the delivery of Spectrum design's &lt;a href="https://spectrum.adobe.com/page/help-text/" rel="noopener noreferrer"&gt;Help Text pattern&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help text
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0r3wsbv3sm5rpt93h8y1.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0r3wsbv3sm5rpt93h8y1.png" alt="Spectrum help text pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Spectrum’s help text pattern:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;provides either an informative description or an error message that gives more context about what a user needs to input. It’s commonly used in forms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Clearly, help text isn’t much use on its own. There needs to be some way to associate help text with the element it describes. Traditionally, that might look like:&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;input&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    The above input is described by this help text.
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;However, we are gathered here today to celebrate the awesomeness that are &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s&lt;/a&gt;, so we have to be talking about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer"&gt;shadow DOM&lt;/a&gt; (required to leverage browser native &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements and their various capabilities), and are likely talking about &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements" rel="noopener noreferrer"&gt;custom elements&lt;/a&gt; (because they all get along so well). With these two APIs together, it's not uncommon to see a form element writ into a custom element as follows:&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;custom-form-element&amp;gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;From here, you're likely to see many APIs structured in a "component as function with properties" pattern that is common in various javascript frameworks and surface &lt;code&gt;help-text&lt;/code&gt; as an attribute of the &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&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;custom-form-element&lt;/span&gt;
    &lt;span class="na"&gt;help-text=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text."&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You might then see such an API expanded for help text across various states:&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;custom-form-element&lt;/span&gt;
    &lt;span class="na"&gt;help-text=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text."&lt;/span&gt;
    &lt;span class="na"&gt;help-text-invalid=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text when invalid."&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Possibly, ad infinitum:&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;custom-form-element&lt;/span&gt;
    &lt;span class="na"&gt;help-text=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text."&lt;/span&gt;
    &lt;span class="na"&gt;help-text-valid=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text when valid."&lt;/span&gt;
    &lt;span class="na"&gt;help-text-invalid=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text when invalid."&lt;/span&gt;
    &lt;span class="na"&gt;help-text-when-you-appear-stuck=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text when you appear stuck."&lt;/span&gt;
    &lt;span class="na"&gt;help-text-etc=&lt;/span&gt;&lt;span class="s"&gt;"An input inside of this element is described by this help text, etc."&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This API had only just started to talk about the &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt; and already it is quite thick (imagine it with actual API for customizing the associated form element). This could certainly weigh on your consuming developers. What's more, while this is going to look (and work) great when javascript is on, or look great (though possibly not work) when delivered in a browser with &lt;a href="https://web.dev/declarative-shadow-dom/" rel="noopener noreferrer"&gt;Declarative Shadow DOM&lt;/a&gt;, it has no chance of looking good with neither (unless &lt;em&gt;generally&lt;/em&gt; not seeing something counts as it looking good, which is true... &lt;em&gt;sometimes&lt;/em&gt;), and it certainly doesn't use any &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s!&lt;/p&gt;

&lt;p&gt;To be clear, none of the notes above are inherently bad. Each of these could align with the desired philosophy of an element, a library of elements, or an application that leverages elements. I call them out here as a way to get to the point I'd like to make about &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;s. If your use case doesn't direct you towards an HTML-like API for your custom elements, more power to you. However, supplying this content as HTML allows us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;customize the DOM element that wraps your help text content&lt;/li&gt;
&lt;li&gt;deliver DOM in that content (e.g. anchor tags, icons, etc.)&lt;/li&gt;
&lt;li&gt;encapsulate any default functionality or styles belonging to help text content&lt;/li&gt;
&lt;li&gt;separate the concerns of delivering a form element from those of delivering help text content&lt;/li&gt;
&lt;li&gt;style help text content directly from the outside&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To support all these things, I'd like to propose the following API.&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;custom-form-element&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      An input inside of this element is described by this help text.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"negative-help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      An input inside of this element is described by this help text when invalid.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The above makes the presence and the customization of content beyond the initial help message easy to manage regardless of the context from which you're delivering it. You've got a custom form element; you slot in the default and negative help text messages and automatically they are correctly associated with the appropriate element within the parent's shadow DOM and hidden/shown based on the validity of the said parent. Similarly, you can slot in a single piece of help text, and it can be fully controlled from the JS scope in which is delivered:&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;custom-form-element&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      An input inside of this element is described by this help text.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's look at how we can bring all of this into being.&lt;/p&gt;

&lt;h2&gt;
  
  
  &amp;lt;slot/&amp;gt;ing in some help text
&lt;/h2&gt;

&lt;p&gt;For this last example, a single slot named &lt;code&gt;help-text&lt;/code&gt; is all you need to get started. Here's what that could look like in a no dependency custom element where we're taking nothing else into account except slotting in this one piece of content:&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="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input /&amp;gt;
  &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomFormElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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="s2"&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="nf"&gt;appendChild&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="nf"&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-form-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomFormElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If this already has you excited to code, hop on over to &lt;a href="https://webcomponents.dev/" rel="noopener noreferrer"&gt;webcomponents.dev&lt;/a&gt; and &lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P?branch=00-slot%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3" rel="noopener noreferrer"&gt;fork the project from this point&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What we've got here &lt;em&gt;is&lt;/em&gt; relatively simple to start, so we've got lots of growing to do. Before we dive into supporting the &lt;code&gt;help-text&lt;/code&gt; &lt;em&gt;AND&lt;/em&gt; &lt;code&gt;negative-help-text&lt;/code&gt; slots, where we can really dig into the concept of Stacked Slots, let's be sure that the content in this slot can be appropriately associated with the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element that we're basing our &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt; around for the time being.&lt;/p&gt;

&lt;p&gt;All form controls need to be supplied with a label (something we are actively omitting at this time) to be accessible. Labels can be associated with a form control as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the form control can supply this content itself via the &lt;code&gt;aria-label&lt;/code&gt; attribute&lt;/li&gt;
&lt;li&gt;a secondary element can be referenced by ID in the form control's &lt;code&gt;aria-labelledby&lt;/code&gt; attribute, or&lt;/li&gt;
&lt;li&gt;the form control itself can be referenced by ID in a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element's &lt;code&gt;for&lt;/code&gt; attribute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Content associated in this way will be read as part of the primary description of the form control. Help text content doesn't require this level of priority in the element's description, so we will associate it by leveraging the form control's &lt;code&gt;aria-describedby&lt;/code&gt; attribute to reference this content by ID.&lt;/p&gt;

&lt;p&gt;When referencing content by ID, it is important to remember that the elements on either side of the reference need to share a DOM tree for the reference can be completed. With our form control (the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;) being within our shadow root and our &lt;code&gt;&amp;lt;custom-help-text&amp;gt;&lt;/code&gt; element being slotted from the outside we can't simply add an ID on one and point to it from the other. Instead, we'll place our &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element into a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; itself and give that &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; the ID to reference from our form control. In this way, the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; can adopt the help text content projected onto the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element and make it available to the form control to reference via ID with its &lt;code&gt;aria-describedby&lt;/code&gt; attribute.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input aria-describedby="help-text" /&amp;gt;
  &amp;lt;div id="help-text"&amp;gt;
    &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/div&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;&lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P?branch=01-associated%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3" rel="noopener noreferrer"&gt;See it in living color&lt;/a&gt;, fork it, share it, and when you're done, come right back, so we can dig even deeper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now for the superpowers
&lt;/h2&gt;

&lt;p&gt;That's right, custom elements allow you to give your HTML superpowers. A consumer of our element could already take what we've made, leverage the bubbling and composed events out of the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element, and manage the text that is supplied to the &lt;code&gt;&amp;lt;custom-help-text&amp;gt;&lt;/code&gt; element from the outside, and we'll see what that looks like shortly. However, we can save most consumers a lot of work by baking some basic management directly into our custom element. This is exactly what is outlined in the code example above:&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;custom-form-element&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Describe interests you would like to explore.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"negative-help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Enter at least one interest.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;While there is much that can be done about managing when something deserves "negative help text", for our demo we'll only track whether our &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; has content or not. To do so, and rather naively at that, we'll add the &lt;code&gt;required&lt;/code&gt; attribute. This will allow us to leverage the &lt;code&gt;checkValidity()&lt;/code&gt; method on the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element to confirm whether any content has been supplied.&lt;/p&gt;

&lt;p&gt;To support this, we'll expand our &lt;code&gt;constructor&lt;/code&gt; to bind a listener for the &lt;code&gt;input&lt;/code&gt; event to our host element:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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="s2"&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="nf"&gt;appendChild&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="nf"&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="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;input&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;handleInput&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;Adding the listener to &lt;code&gt;this&lt;/code&gt;, even though the &lt;code&gt;input&lt;/code&gt; occurs within our shadow DOM, is useful to ensure that the &lt;code&gt;handleInput()&lt;/code&gt; callback is bound to our host element, and not the child &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;. In our example, we'll be keeping the held state to an absolute minimum, but having &lt;code&gt;this&lt;/code&gt; reference the host element not only allows us to directly query selectors within our element, were we to hold state on our element we'd have access to that as well.  In the &lt;code&gt;handleInput()&lt;/code&gt; callback we can examine the &lt;code&gt;event&lt;/code&gt;'s &lt;code&gt;composedPath()&lt;/code&gt; to reacquire the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element and call &lt;code&gt;checkValidity()&lt;/code&gt; on it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;handleInput&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="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&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;composedPath&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNegative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;target&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We'll leverage the &lt;code&gt;showNegative&lt;/code&gt; variable to tell our element to show the negative help text. &lt;code&gt;showNegative&lt;/code&gt; as a variable name is very specific to our help text use case, and that shouldn't be an issue with it scoped to our &lt;code&gt;handleInput()&lt;/code&gt; method. In larger scopes of work, we should look at making this attribute more semantic to our &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt;. This might be managing it as &lt;code&gt;invalid&lt;/code&gt;, or similar, that would make sense to surface via the element's public API.&lt;/p&gt;

&lt;p&gt;While we're here, we will also surface a getter/setter pair to manage the &lt;code&gt;value&lt;/code&gt; of our &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; from the outside:&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;get&lt;/span&gt; &lt;span class="nf"&gt;value&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="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="nf"&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;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&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="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="nf"&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&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;Next we'll get into what we actually do with these values.&lt;/p&gt;

&lt;h3&gt;
  
  
  All the slots
&lt;/h3&gt;

&lt;p&gt;So far, we've discussed the consumption of two different slots: &lt;code&gt;help-text&lt;/code&gt; and &lt;code&gt;negative-help-text&lt;/code&gt;. Why?&lt;/p&gt;

&lt;p&gt;Well, some consumers want all the control. In this use case, we surface the &lt;code&gt;help-text&lt;/code&gt; slot so that absolutely anything can be put into it, whenever the parent application would like. Want to cheer your visitor on for every keystroke they make, this is the slot for that.&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;custom-form-element&lt;/span&gt;
    &lt;span class="na"&gt;oninput=&lt;/span&gt;&lt;span class="s"&gt;"
      const modiferElement = this.querySelector('#modifier');
      const countElement = this.querySelector('#count');
      const multipleElement = this.querySelector('#multiple');
      const length = this.value.length;
      multipleElement.textContent = length === 1
        ? ''
        : 's';
      countElement.textContent = length;
      let modiferText = '';
      if (length &amp;gt; 10) {
        modiferText = 'Wow!';
      } else if (length &amp;gt; 5) {
        modiferText = 'Nice.';
      } else if (length &amp;gt; 0) {
        modiferText = 'Keep going.';
      }
      modiferElement.textContent = modiferText;
    "&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&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;"modifier"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt; You've typed &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;"count"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt; character&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;"multiple"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;s&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;.
    &lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Really, with the &lt;code&gt;help-text&lt;/code&gt; slot, &lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P/src/index.js?branch=02-value%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3" rel="noopener noreferrer"&gt;access to the form control's value&lt;/a&gt;, and experience with which to interact with it from the outside, there's no end to how you could leverage the content you might supply. However, in more cases than not, swapping between "this is what you should do" and "this is how you get out of the problem you've gotten yourself in" text will likely support the goals of our consumers. In some cases, we might just be turning on the "this is how you get out of the problem you've gotten yourself in" text. Pairing the &lt;code&gt;help-text&lt;/code&gt; slot with a &lt;code&gt;negative-help-text&lt;/code&gt; slot, we can make the process of doing these things something they'll &lt;em&gt;almost&lt;/em&gt; never have to think about.&lt;/p&gt;

&lt;p&gt;So, where are we actually inserting these slots into our element's shadow DOM? We might start by positioning them next to each other, as siblings:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input aria-describedby="help-text" /&amp;gt;
  &amp;lt;div id="help-text"&amp;gt;
    &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;slot name="negative-help-text"&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/div&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;From here we could leverage the &lt;code&gt;showNegative&lt;/code&gt; variable we've already derived in our &lt;code&gt;handleInput()&lt;/code&gt; method to do something like add the &lt;code&gt;hidden&lt;/code&gt; attribute (ignoring that &lt;a href="https://meowni.ca/hidden.is.a.lie.html" rel="noopener noreferrer"&gt;[hidden] is a lie&lt;/a&gt;) to the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements conditionally. Then we can show the &lt;code&gt;negative-help-text&lt;/code&gt; slot when &lt;code&gt;!!showNegative&lt;/code&gt;. When &lt;code&gt;!showNegative&lt;/code&gt;, we can show the &lt;code&gt;help-text&lt;/code&gt; slot. And, we're done.&lt;/p&gt;

&lt;p&gt;Except, what happens when content is only addressed to the &lt;code&gt;help-text&lt;/code&gt; slot?&lt;/p&gt;

&lt;p&gt;Toggling &lt;code&gt;hidden&lt;/code&gt; directly on both &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements in response to &lt;code&gt;showNegative&lt;/code&gt; would mean that the content addressed to the &lt;code&gt;help-text&lt;/code&gt; slot would be hidden when &lt;code&gt;showNegative&lt;/code&gt;, regardless of whether there was content to display in the &lt;code&gt;negative-help-text&lt;/code&gt; slot at that time. We could &lt;em&gt;only&lt;/em&gt; toggle &lt;code&gt;hidden&lt;/code&gt; on the &lt;code&gt;negative-help-text&lt;/code&gt; slot, but that would mean that there are times that our &lt;code&gt;&amp;lt;custom-from-element&amp;gt;&lt;/code&gt; would receive two pieces of help text. Some component authors might want to deliver exactly this functionality to their users, in which case, they'll be ready to go with the above. For those of you that would agree, &lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P/stories/index.stories.js?branch=03-both@wfVpXdsTYhf9mY2slxW4t2Y2SjC3" rel="noopener noreferrer"&gt;here's your off ramp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For those of you who, like me, see more than one type of help text as something to prevent, there are a couple of options available. One would be to leverage the &lt;code&gt;slotchange&lt;/code&gt; event, and the &lt;code&gt;assignedElements()&lt;/code&gt; API on the &lt;code&gt;negative-help-text&lt;/code&gt; slot to decide whether it has content, and when it does use that state in concert with &lt;code&gt;showNegative&lt;/code&gt; to decide when to hide the &lt;code&gt;help-text&lt;/code&gt; slot. One more event listener, one more callback method, one quick question about &lt;code&gt;slotchange&lt;/code&gt; timing and whether you should hold state instead, and you'd be ready to go! But, what if I told you that the browser already had this functionality built directly into it?&lt;/p&gt;

&lt;p&gt;Well, it does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stacked slots
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element doesn't just act as a marker for where content can be projected into your element from the outside. The &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element can also supply default content to be delivered when there is nothing projected onto it, as well. What's more, that content can be additional &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements. In this way, we can stack our slots and give the earliest ancestor &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; in the stack precedence over those that descend from it. For our help text slots, that could look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input aria-describedby="help-text" /&amp;gt;
  &amp;lt;div id="help-text"&amp;gt;
    &amp;lt;slot name="negative-help-text"&amp;gt;
      &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/slot&amp;gt;
  &amp;lt;/div&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 allows any content addressed to the &lt;code&gt;negative-help-text&lt;/code&gt; slot to "win" and be the content that is shown when it is available. When that is absent, content addressed to the &lt;code&gt;help-text&lt;/code&gt; slot will always be available for users to manage directly from the outside. That means that when HTML like the following is used, only the "This field is required!" content that is addressed to the &lt;code&gt;negative-help-text&lt;/code&gt; slot will be displayed on the rendered page.&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;custom-form-element&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Please type something here.&lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-help-text&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"negative-help-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This field is required!&lt;span class="nt"&gt;&amp;lt;/custom-help-text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/custom-form-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This isn't exactly what we were looking for, so we need to add a little something more. We want our parent &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; to pass through to our child &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; when the form control's value is valid. To do this, instead of relying on &lt;code&gt;hidden&lt;/code&gt; to show or hide the element, we can use a nonsensical &lt;code&gt;name&lt;/code&gt; to ensure content can't be addressed to the slot.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input aria-describedby="help-text" /&amp;gt;
  &amp;lt;div id="help-text"&amp;gt;
    &amp;lt;slot
      name="pass-through-help-text-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;"
      id="negative-help-text"
    &amp;gt;
      &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/slot&amp;gt;
  &amp;lt;/div&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 will be our default, allowing the &lt;code&gt;help-text&lt;/code&gt; slot to take precedence. While handling the &lt;code&gt;input&lt;/code&gt; event we can then use the value of &lt;code&gt;showNegative&lt;/code&gt; to toggle the &lt;code&gt;name&lt;/code&gt; attribute to something addressable.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;


&lt;span class="nf"&gt;handleInput&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="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&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;composedPath&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNegative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;target&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slot&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#negative-help-text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;showNegative&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;negative-help-text
    : `pass-through-help-text-${Math.random()}`;
}


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

&lt;/div&gt;

&lt;p&gt;This will give our content addressed to &lt;code&gt;negative-help-text&lt;/code&gt; a slot onto which to be projected when our form control becomes &lt;code&gt;showNegative&lt;/code&gt; without preventing content addressed to the &lt;code&gt;help-text&lt;/code&gt; slot from being displayed when &lt;code&gt;negative-help-text&lt;/code&gt; content is absent. It's a bit like API validation in HTML.&lt;/p&gt;

&lt;p&gt;With all of this together, we'll be imbuing our &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt; with the following super powers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accessible help text content&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;help-text&lt;/code&gt; slot to act as default and receive updates from the outside while displaying in all validity states&lt;/li&gt;
&lt;li&gt;a conditional &lt;code&gt;negative-help-text&lt;/code&gt; slots for overriding that content with content meant only for when the form control should &lt;code&gt;showNegative&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a neat little custom element who's definition looks about like:&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="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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="cm"&gt;/*html*/&lt;/span&gt;&lt;span class="s2"&gt;`
  &amp;lt;input
    aria-describedby="help-text"
    required
  /&amp;gt;
  &amp;lt;div id="help-text"&amp;gt;
    &amp;lt;slot
      id="contextual-help-text"
      name="pass-through-help-text-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;"
    &amp;gt;
      &amp;lt;slot name="help-text"&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/slot&amp;gt;
  &amp;lt;/div&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomFormElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;value&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="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="nf"&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;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;value&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="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="nf"&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;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="s2"&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="nf"&gt;appendChild&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="nf"&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="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;input&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;handleInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;handleInput&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;contextualHelpTextSlot&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;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#contextual-help-text&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;target&lt;/span&gt; &lt;span class="o"&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;composedPath&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNegative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;target&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="nx"&gt;contextualHelpTextSlot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;showNegative&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;negative-help-text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`pass-through-help-text-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-form-element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomFormElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P/src/index.js?branch=04-contextual%40wfVpXdsTYhf9mY2slxW4t2Y2SjC3&amp;amp;p=stories" rel="noopener noreferrer"&gt;Check it out more closely&lt;/a&gt;, as well as demos for working with the &lt;code&gt;help-text&lt;/code&gt; and &lt;code&gt;negative-help-text&lt;/code&gt; slots to varying levels of complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's missing?
&lt;/h2&gt;

&lt;p&gt;Before we wrap up, let's visit a (not so short) list of things that were consciously omitted from the conversation in this article. If you find any that I've unconsciously missed, or if one of these is of particular interest to you, please let me know in the comments below as I'd want to get it added when missing or take your interest as a nice push towards any sequel posts with which I might follow this.&lt;/p&gt;

&lt;p&gt;The first to come to mind is giving a definition to our &lt;code&gt;&amp;lt;custom-help-text&amp;gt;&lt;/code&gt; element that is features throughout our demos but until this point has no superpowers of its own.&lt;/p&gt;

&lt;p&gt;Revisiting our screenshot of what help text could look like, we've clearly skipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;labeling our form element&lt;/li&gt;
&lt;li&gt;supporting API like &lt;code&gt;required&lt;/code&gt; (see the * mark) on &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;styling content that we build this way. Yes, I noticed that the visuals in that image are much more appealing than our demos to date&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thinking about the validity and value piping that we added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what other APIs (beyond &lt;code&gt;value&lt;/code&gt;) of our form element should be passed through to our &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;what other forms of validity (beyond &lt;code&gt;required&lt;/code&gt;) should be available and how can we surface them?&lt;/li&gt;
&lt;li&gt;how can we make this validity and value a part of a parent &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element's form data? (Yes, the shadow DOM boundary effectively hides the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; from participating in our custom element's current form.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at how we related our help text to our form control, we might also be interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supporting form controls that are outside of our shadow root&lt;/li&gt;
&lt;li&gt;abstracting this relationship into a reusable form that could empower many custom form control elements, rather than just one&lt;/li&gt;
&lt;li&gt;the benefits of simplifying this code with the help of a declarative templating system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clearly, there is a lot that we could do to continue to expand on what we've started here together. Some might even involve more learning and praise around &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements, but most spread across the breadth of APIs and capabilities that are available in the browser today as web components.&lt;/p&gt;

&lt;p&gt;But, just because there is a lot to do, doesn't mean we haven't already achieved a lot together. &lt;/p&gt;

&lt;h2&gt;
  
  
  How far we've come
&lt;/h2&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzrdrxqzeko9jpq3zp7c.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzrdrxqzeko9jpq3zp7c.png" alt="A screen shot of all of the stories we've created."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At press time, &lt;a href="https://webcomponents.dev/edit/sTddx519tvSvSKT0984P/src/index.js?branch=02-validity@wfVpXdsTYhf9mY2slxW4t2Y2SjC3" rel="noopener noreferrer"&gt;our demo&lt;/a&gt; features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a reusable &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;a single &lt;code&gt;required&lt;/code&gt; &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element on which we track validity&lt;/li&gt;
&lt;li&gt;piping for seeing the value of the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; from the outside&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; is accessibly related to "help text" content that can be supplied from the outside as HTML&lt;/li&gt;
&lt;li&gt;a default &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; (&lt;code&gt;help-text&lt;/code&gt;) surfaces a wide array of from the outside customizations around the delivery of the help text &lt;/li&gt;
&lt;li&gt;an override &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; (&lt;code&gt;negative-help-text&lt;/code&gt;) that is available when the &lt;code&gt;&amp;lt;custom-from-element&amp;gt;&lt;/code&gt; is &lt;code&gt;showNegative&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do so we've learned about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building a custom element from scratch&lt;/li&gt;
&lt;li&gt;how amazing &lt;a href="https://webcomponents.dev/" rel="noopener noreferrer"&gt;webcomponents.dev&lt;/a&gt; is&lt;/li&gt;
&lt;li&gt;ways to manage property/attribute bloat&lt;/li&gt;
&lt;li&gt;accessibly relating content from the outside of a custom element to content with that element's shadow DOM&lt;/li&gt;
&lt;li&gt;bind events to custom elements&lt;/li&gt;
&lt;li&gt;some basics around boolean attributes&lt;/li&gt;
&lt;li&gt;making state on elements encapsulated within our shadow root public&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements and their default content&lt;/li&gt;
&lt;li&gt;stacking &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully, you've found it interesting and educational. Feel free to share anything I could be more clear about in the comments below so that this content can be even more useful for our readers.&lt;/p&gt;

&lt;p&gt;After that, you can visit a more advanced version of this technique where it is being leveraged in &lt;a href="https://opensource.adobe.com/spectrum-web-components/components/help-text/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt;. There you'll see this functionality delivered as a class factory mixin into custom elements based on the &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;lit&lt;/a&gt; library and its &lt;a href="https://lit.dev/docs/api/LitElement/" rel="noopener noreferrer"&gt;&lt;code&gt;LitElement&lt;/code&gt;&lt;/a&gt; base class, great fodder for follow up conversations around lending declarative templating and reactivity to our &lt;code&gt;&amp;lt;custom-form-element&amp;gt;&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Additional editing by &lt;a href="https://twitter.com/HunterLoftis" rel="noopener noreferrer"&gt;@HunterLoftis&lt;/a&gt;, who wished after the fact that I had asked him to read this before I asked him to review a PR based on these ideas.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@aarez?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Aarón González&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/slot?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>shadowdom</category>
      <category>customelements</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Some things to know about Lit</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Mon, 04 Oct 2021 03:04:18 +0000</pubDate>
      <link>https://dev.to/open-wc/some-things-to-know-about-litelement-282c</link>
      <guid>https://dev.to/open-wc/some-things-to-know-about-litelement-282c</guid>
      <description>&lt;p&gt;When reviewing software with which you have little experience, it's pretty common to attempt to compare it to software you have used before. That can help you get a handle on the general ergonomics and decisions behind the two pieces of software in question. However, one thing that this approach is not particularly good at is comparing the new software &lt;em&gt;in situ&lt;/em&gt;. You may have spent a good amount of time investigating, researching, and getting comfortable with the software you're already using in the context of your particular use case, so much so, that it may even be the best solution available in that context. Comparing a new software in that context (unless it, too, is purpose-built for that context) can do a disservice to the software under test, as well as to your ability to fully understand the benefits of that software.&lt;/p&gt;

&lt;p&gt;This is what I keep coming back to when people say things like "X isn't as good as Y" when "X" is a way to build web components (&lt;a href="https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/" rel="noopener noreferrer"&gt;of which there are many&lt;/a&gt;) and "Y" is a JS framework. Even before you fill actual names into either side of the equation the differences in usage of what you fill those in with is important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JS frameworks often want to own most or all of a page, whereas a web component is a single custom element on an potentially large and diverse DOM tree.&lt;/li&gt;
&lt;li&gt;Functional JS frameworks often hide much of that ownership from view to both the benefit (less code) and expense (less flexibility) of developers leveraging them.&lt;/li&gt;
&lt;li&gt;JS Frameworks tend to be an abstraction above the DOM and in this way, their "components" can exist as both literal (DOM/UI elements) and figurative (data connectivity/translations to non-web contexts).&lt;/li&gt;
&lt;li&gt;Web components being a DOM element can interact with their position in the DOM in the ways you'd otherwise need to bend over backward to do in a framework.&lt;/li&gt;
&lt;li&gt;JS frameworks often ship a lot of JS down the wire that may not be required by your application or component(s) whether or not the framework author gives you the ability to manage how much of that code makes it into your production build.&lt;/li&gt;
&lt;li&gt;Web components are portable to just about any context in which you'd build web UI, while JS frameworks require your components to run in an app built with the same framework (unless or course, they allow you to export web components).&lt;/li&gt;
&lt;li&gt;and, many, many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some or all of these points could be seen as being for or against either side of the conversation. Many of these could switch from pro to con depending on the particular use case. Too often, reviews that fail to take that into account lead to half-baked, often hypercritical takes.&lt;/p&gt;

&lt;p&gt;With that in mind, I want to go over some concepts that support healthier decision-making when teams evaluate LitElement for their projects. This isn't really a "how-to", though I've got some (slightly) dated version of that available in my &lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;"Not Another To-Do App" series&lt;/a&gt;. This is more of a "good to know" guide, as these concepts aren't all exact ports from JS framework contexts. I hope the ability to judge the differences from a place of knowledge proves useful to you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Default Values
&lt;/h2&gt;

&lt;p&gt;There are a number of ways to define default values for a property on a LitElement, While some do require management across the entire element class, we'll ignore those today as there are a number of options that do not require such work.&lt;/p&gt;

&lt;p&gt;First off, LitElement's &lt;code&gt;render()&lt;/code&gt; method of a LitElement is (from the user's perspective) an almost 1 to 1 conversion from the functional definitions found in other offerings. In this way, you could treat &lt;code&gt;render()&lt;/code&gt; as the only entry into your properties and define fallbacks at the top of your render function's body like so:&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="nf"&gt;render&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;closeDelay&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;closeDelay&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;300&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;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;x-dialog delay=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;closeDelay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Thank you!&amp;lt;/x-dialog&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;From here, you could get very close to copy and paste the rest of a functional UI component into a LitElement for an early test of its capabilities. Leveraging scoped variables like this, rather than class properties, does leave you in much the same place that functional alternatives to UI development do; needing additional tools for memoization of that scope. As using LitElement means you're already in a class context, we can use the capabilities of a class directly rather than synthesizing them with memoization.&lt;/p&gt;

&lt;p&gt;A simpler approach to merging the two concepts is to set that fallback into the class property itself. Above I showed doing this in the &lt;code&gt;render()&lt;/code&gt; lifecycle method, however, I find it much nicer to &lt;em&gt;only&lt;/em&gt; have the template surfaced therein. Leveraging one of the earlier lifecycle methods for managing defaults, validation, sanitation, transformation, or derivation help to maintain that structure. In this case, we'll use &lt;code&gt;willUpdate()&lt;/code&gt; which doesn't require a &lt;code&gt;super&lt;/code&gt; call or a returned value, but will always be visited during each render lifecycle:&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="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropertyValues&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&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;void&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;closeDelay&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;closeDelay&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;300&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;With this approach, you can most closely facilitate the single line defaulting that can be present when relying on a function to define a component. This does mean the &lt;code&gt;shouldRender()&lt;/code&gt; method - the first method called in the render lifecycle - will not have your default value, and if that's an issue for your style of element development, you might want to move this fallback work there, but it also means that due to the fact the LitElement renders asynchronously there's technically a possibility that your local methods could as well. Due to this fact, you may want to leverage a slightly more complete approach to a default.&lt;/p&gt;

&lt;p&gt;Here we see the property getter fallback to the value:&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;get&lt;/span&gt; &lt;span class="nf"&gt;closeDelay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&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;_closeDelay&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;closeDelay&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;number&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;closeDelay&lt;/span&gt; &lt;span class="o"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeDelay&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;closeDelay&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closeDelay&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;closeDelay&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;_closeDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;closeDelay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;_closeDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does eclipse the simplicity of a functional solution as we are choosing to apply the default by writing our own getter/setter pair on the property. At the same time, however, we've fallen into a possible trap of the functional fallback approach (and as the &lt;code&gt;willUpdate()&lt;/code&gt; approach above), we're allowing our entire render lifecycle to be triggered for what might not be an actual change to our component state. Were &lt;code&gt;this.closeDelay&lt;/code&gt; to already equal &lt;code&gt;300&lt;/code&gt; and the application to change it to &lt;code&gt;undefined&lt;/code&gt;, all of these approaches we've looked at so far would cause whatever could occur in your render lifecycle to occur needlessly.&lt;/p&gt;

&lt;p&gt;By falling back in the setter as opposed to the getter you can leverage the capabilities of a class component to prevent the render lifecycle to be started altogether. In the following code, no matter how the value of &lt;code&gt;this.closeDelay&lt;/code&gt; gets to &lt;code&gt;300&lt;/code&gt; the call to &lt;code&gt;this.requestUpdate()&lt;/code&gt; is gated as long as the final value doesn't change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyThing&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;closeDelay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;_closeDelay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;closeDelay&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;number&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;closeDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;300&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;closeDelay&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;closeDelay&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;closeDelay&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;closeDelay&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;_closeDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;closeDelay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;_closeDelay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&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 you may still ask, "but, why it is so much more code?" and, in our ecosystem of "less is more", both from a DX as well as a UX/performance standpoint, it's a great question. It's more code because it is also a different level/type of capability. Here we get a default to our property, clear gating on the render lifecycle, and on top of that, we get a value that is held state-fully within a class that defines a DOM element. This means that not only can it take part in the render pipeline of the element that owns it, but that it is available for other elements that share its DOM tree to query as a container for that state. Not every application is architected with a want or need for this capability. Not every component is going to be leveraged at the scale where the checking is needed to confirm that the render lifecycle has no side effects in a way that benefits extra prevention of the lifecycle altogether. However, when you do, you might take a look at LitElement as a path towards attaining these capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  So you like Typescript?
&lt;/h2&gt;

&lt;p&gt;Typescript loves a good &lt;code&gt;"Property is not definitely assigned in the constructor"&lt;/code&gt; warning, and if you like Typescript enough to use it a lot, you'll likely run into it at some point. It's telling you this, because in contrast to what you've been told Typescript is NOT smarter than you and it can't tell if something &lt;em&gt;should&lt;/em&gt; always be available, only if it &lt;em&gt;might&lt;/em&gt; not be available. You can set an initial value to a property, and it'll never yell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyThing&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;myProperty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// always available, always a string&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're looking for that to &lt;em&gt;have&lt;/em&gt; to be initialized to use your element, so &lt;em&gt;you&lt;/em&gt; KNOW it's going to always have a value, but you want the consumer to initialize it, then you can tell Typescript that by using the &lt;code&gt;!&lt;/code&gt; operator once and be done with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyThing&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;myProperty&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// no initial value, but a string is required from the consumer&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 still want to be defensive, you can add some helper code in your lifecycle to support a consumer leveraging your custom element correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyThing&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;shouldUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropertyValues&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&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;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// great location to make sure it's NEVER undefined;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canUpdate&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;myProperty&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&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;canUpdate&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="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;`myProperty` is unset&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="nx"&gt;canUpdate&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;firstUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropertyValues&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&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;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// for one time availability confirmation&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;firstUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myProperty&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="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;`myProperty` is unset&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;h3&gt;
  
  
  Required properties
&lt;/h3&gt;

&lt;p&gt;In this way, you can also manage required properties/attributes. No, it doesn't fall within the available management of a tool-based contract with the consumers of your component, however, a tool-based contract is not strictly enforceable. You as a component author can tell Typescript or a linter to error on certain things, but your consumer can tell them not to just as easily. Deciding to &lt;em&gt;only&lt;/em&gt; leverage tooling for this sort of capability might mean less work for you, but it doesn't guarantee better outcomes for your consumers. Any component author will need to decide the risks they are willing to foist onto their consumers when publishing a component, and this is yet another item to manage on that list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event management
&lt;/h2&gt;

&lt;p&gt;Event listeners added directly on &lt;code&gt;this&lt;/code&gt; in a custom element &lt;em&gt;do not&lt;/em&gt; need to be cleaned up when disconnected from the &lt;code&gt;document&lt;/code&gt;. Once all references to the element are released, the same garbage collection that cleans up the element itself will clean up the events bound to it. What's more, when calling &lt;code&gt;addEventListener&lt;/code&gt; on &lt;code&gt;this&lt;/code&gt;, the method's &lt;code&gt;this&lt;/code&gt; reference automatically reverts to the instance. You don't need to bind the method, so you can call a class method directly without any &lt;code&gt;.bind(this)&lt;/code&gt; or class field arrow-functions.&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu-trigger&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;class&lt;/span&gt; &lt;span class="nc"&gt;MenuTrigger&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&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;trigger&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PropertyValues&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&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;void&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;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trigger&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changedProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trigger&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;eventHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventHandler&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="kr"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do stuff.&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;Events are even easier if you know the name of the event you're wanting to listen for will be the same throughout the lifecycle of your application. With that knowledge you can listen just once without needing to add/remove the listener based on even name changes over time:&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="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;menu-trigger&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;class&lt;/span&gt; &lt;span class="nc"&gt;MenuTrigger&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;known-event-name&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;eventHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// do stuff.&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;Done and done!&lt;/p&gt;

&lt;h2&gt;
  
  
  styleMap usage
&lt;/h2&gt;

&lt;p&gt;Lit's &lt;code&gt;styleMap()&lt;/code&gt; directive helps when setting the &lt;code&gt;style&lt;/code&gt; attribute on HTML elements from JavaScript. It accepts an object with css-property keys and string, &lt;code&gt;undefined&lt;/code&gt;, or &lt;code&gt;null&lt;/code&gt; values. This means you can prevent a CSS property from being added to the element by passing &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt; as the value, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styleMap&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;lit/directives/style-map.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;p
        style=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;styleMap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px solid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;200px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// I don't show a type mismatch&lt;/span&gt;
          &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// I do show a type mismatch&lt;/span&gt;
        &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;gt;Hello, world!&amp;lt;/p&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;&lt;a href="https://lit.dev/playground/#project=W3sibmFtZSI6InNpbXBsZS1ncmVldGluZy50cyIsImNvbnRlbnQiOiJpbXBvcnQge2h0bWwsIGNzcywgTGl0RWxlbWVudH0gZnJvbSAnbGl0JztcbmltcG9ydCB7Y3VzdG9tRWxlbWVudCwgcHJvcGVydHl9IGZyb20gJ2xpdC9kZWNvcmF0b3JzLmpzJztcbmltcG9ydCB7c3R5bGVNYXB9IGZyb20gJ2xpdC9kaXJlY3RpdmVzL3N0eWxlLW1hcC5qcyc7XG5cbkBjdXN0b21FbGVtZW50KCdzaW1wbGUtZ3JlZXRpbmcnKVxuZXhwb3J0IGNsYXNzIFNpbXBsZUdyZWV0aW5nIGV4dGVuZHMgTGl0RWxlbWVudCB7XG4gIHN0YXRpYyBzdHlsZXMgPSBjc3NgcCB7IGNvbG9yOiBibHVlIH1gO1xuXG4gIEBwcm9wZXJ0eSgpXG4gIG5hbWUgPSAnU29tZWJvZHknO1xuXG4gIHJlbmRlcigpIHtcbiAgICByZXR1cm4gaHRtbGBcbiAgICAgICAgPHBcbiAgICAgICAgc3R5bGU9JHtzdHlsZU1hcCh7XG4gICAgICAgICAgYm9yZGVyOiAnMXB4IHNvbGlkJyxcbiAgICAgICAgICB3aWR0aDogJzIwMHB4JyxcbiAgICAgICAgICBmbG9hdDogdW5kZWZpbmVkLFxuICAgICAgICAgIG1hcmdpbjogMTAsXG4gICAgICAgIH0pfVxuICAgICAgICA-SGVsbG8sICR7dGhpcy5uYW1lfSE8L3A-YDtcbiAgfVxufVxuIn0seyJuYW1lIjoiaW5kZXguaHRtbCIsImNvbnRlbnQiOiI8IURPQ1RZUEUgaHRtbD5cbjxoZWFkPlxuICA8c2NyaXB0IHR5cGU9XCJtb2R1bGVcIiBzcmM9XCIuL3NpbXBsZS1ncmVldGluZy5qc1wiPjwvc2NyaXB0PlxuPC9oZWFkPlxuPGJvZHk-XG4gIDxzaW1wbGUtZ3JlZXRpbmcgbmFtZT1cIldvcmxkXCI-PC9zaW1wbGUtZ3JlZXRpbmc-XG48L2JvZHk-XG4ifSx7Im5hbWUiOiJwYWNrYWdlLmpzb24iLCJjb250ZW50Ijoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJsaXRcIjogXCJeMi4wLjAtcmMuMlwiLFxuICAgIFwiQGxpdC9yZWFjdGl2ZS1lbGVtZW50XCI6IFwiXjEuMC4wLXJjLjJcIixcbiAgICBcImxpdC1lbGVtZW50XCI6IFwiXjMuMC4wLXJjLjJcIixcbiAgICBcImxpdC1odG1sXCI6IFwiXjIuMC4wLXJjLjNcIlxuICB9XG59IiwiaGlkZGVuIjp0cnVlfV0" rel="noopener noreferrer"&gt;Check it out here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current version of Lit's styleMap excludes numbers as values. You might expect Lit to automatically convert numbers to &lt;code&gt;px&lt;/code&gt; values, but on second thought that isn't actually what you'd want. In CSS, where &lt;code&gt;px&lt;/code&gt; is just one of many units that a numeric CSS property could accept (&lt;code&gt;%&lt;/code&gt;, &lt;code&gt;vh&lt;/code&gt;, &lt;code&gt;vwmax&lt;/code&gt;, &lt;code&gt;pt&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt;, &lt;code&gt;rem&lt;/code&gt;, &lt;code&gt;pt&lt;/code&gt;, &lt;code&gt;pc&lt;/code&gt;, &lt;em&gt;ad infinitum&lt;/em&gt;), there's no way for Lit to know or even assume what kind of number you're using. On top of that, you might want to apply unit-less numbers directly to your styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XL&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
    div {
      width: 10px;
      height: 10px;
      background-color: hsl(var(--hue, 0) 50 100);
    }
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;hue&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="nf"&gt;render&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;divStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;styleMap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--hue&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;hue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;div style="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;divStyles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;&amp;lt;/div&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks &lt;a class="mentioned-user" href="https://dev.to/bennypowers"&gt;@bennypowers&lt;/a&gt; for the code sample here.&lt;/p&gt;

&lt;p&gt;Defaulting numbers to &lt;code&gt;px&lt;/code&gt; would be a foot-gun. Instead, Lit encourages you to be explicit with your CSS code. Doing so not only helps your consumers, but your teammates and future self as well when it comes time to maintain the components that you create.&lt;/p&gt;




&lt;p&gt;One of the best things about &lt;code&gt;lit-html&lt;/code&gt;, the renderer underlying LitElement, is that, if you want to live on that wild side, you could create your own directive that applied number typed properties as &lt;code&gt;px&lt;/code&gt; and leverage it in your own work. Here are &lt;a href="https://lit.dev/docs/templates/custom-directives/" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; for doing just that! If you're still not convinced, check out some &lt;a href="https://dev.to/open-wc/doing-a-flip-with-lit-html-2-0-3gn4"&gt;directives with which I've experimented&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;NOTE: the above article, "Doing a FLIP with &lt;a href="mailto:lit-html@2.0"&gt;lit-html@2.0&lt;/a&gt;", was written against an RC of &lt;code&gt;lit@2.0&lt;/code&gt; and may not be 100% current. I'll be looking to update it here, soon.&lt;/p&gt;

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

&lt;p&gt;When you're learning a new piece of software, by all means, start by comparing it to something that you know. It's like a cheat code to getting started down the path of learning something new. Once you've done that, don't stop there, get into a real use case with it and learn what sort of capabilities or techniques it unlocks or supports. Only then can you really get into the question of why it's doing so and whether in the context that it is intended to be used (or the context that you might use it) it's the sort of tool you want to leverage for the job.&lt;/p&gt;




&lt;p&gt;If you do get to building something with LitElement, come share it here and let's chat about the whats and whys of what you've done/are trying to do. I look forward to seeing it here in the comments, or hit me up on the &lt;a href="https://join.slack.com/t/lit-and-friends/shared_invite/zt-llwznvsy-LZwT13R66gOgnrg12PUGqw" rel="noopener noreferrer"&gt;Lit &amp;amp; Friends Slack&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>litelement</category>
      <category>webcomponents</category>
      <category>typescript</category>
      <category>learning</category>
    </item>
    <item>
      <title>Doing a FLIP with lit-html@2.0</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Tue, 29 Dec 2020 16:45:00 +0000</pubDate>
      <link>https://dev.to/open-wc/doing-a-flip-with-lit-html-2-0-3gn4</link>
      <guid>https://dev.to/open-wc/doing-a-flip-with-lit-html-2-0-3gn4</guid>
      <description>&lt;p&gt;UPDATE: (20 March 2021) Add support for &lt;code&gt;window.matchMedia('(prefers-reduced-motion: no-preference)')&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;UPDATE: (23 February 2021) Use &lt;code&gt;lit-html@2.0.0-pre.6&lt;/code&gt; and &lt;code&gt;lit-element@3.0.0-pre.3&lt;/code&gt; and their associated API changes.&lt;/p&gt;

&lt;p&gt;There's nothing like a good vacation to get the desire to try out a new piece of technology to grow like a weed in my mind. Especially if it promises to make my work not only easier, but faster and more fun at the same time. Enter the upcoming releases of &lt;code&gt;lit-html&lt;/code&gt; and &lt;code&gt;LitElement&lt;/code&gt;; a powerfully light renderer and a productively simple custom elements base class, respectively. These fine products from the Polymer team at Google have been an important part of my work for going on 3 or so years now, along with many other offerings from the team in the years before that, so my interest was piqued when they released their first preview build of both earlier this year. These initial looks into the new code structure of the two libraries didn't offer much in new features, but each pointed to a powerful new future that the Polymer team had been laying out for itself. So, when a second round of previews was dropped, just before the holiday break, this time supporting both new APIs and features, I couldn't wait to jump in and take a look around.&lt;/p&gt;

&lt;p&gt;First off, if you're interested in the nitty-gritty, I suggest you start by taking a look at the README's for the latest releases of &lt;a href="https://github.com/Polymer/lit-html/blob/lit-next/packages/lit-html/README.md" rel="noopener noreferrer"&gt;&lt;code&gt;lit-html&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/Polymer/lit-html/blob/lit-next/packages/lit-element/README.md" rel="noopener noreferrer"&gt;&lt;code&gt;LitElement&lt;/code&gt;&lt;/a&gt; to get right into all the things that have been or will be changed before a stable release early 2021. There are a lot of cool things, not least of which is the desire to cause as few breaks as possible when moving our use of &lt;code&gt;lit-html@1.0&lt;/code&gt; and &lt;code&gt;LitElement@2.0&lt;/code&gt; to the new versions. The biggest break looks to be in the change from a functional to a class-based API for the directive functionality offered by &lt;code&gt;lit-html&lt;/code&gt;. While I use directives a good amount in my work, I've mainly worked with the ones &lt;a href="https://lit-html.polymer-project.org/guide/template-reference#built-in-directives" rel="noopener noreferrer"&gt;built-in to &lt;code&gt;lit-html&lt;/code&gt;&lt;/a&gt; by default. I'd only really built my own directives once or twice, and being I use these tools to work with custom elements (which are themselves class-based), I agree that this change is for the better of the ecosystem these tools serve. With this simplification of context, I thought directives would be a great place to take a look at what's going to be possible in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  My directives to date
&lt;/h2&gt;

&lt;p&gt;I've recently started working with a "streaming listener" directive in my work with Adobe's &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt; for a number of in-development patterns, to nice success. The &lt;a href="https://open-wc.org/" rel="noopener noreferrer"&gt;Open Web Components&lt;/a&gt; team and I vend a series of &lt;code&gt;lit-helpers&lt;/code&gt;, one of which is a &lt;a href="https://open-wc.org/docs/development/lit-helpers/#spread-directives" rel="noopener noreferrer"&gt;spread directive&lt;/a&gt; for &lt;code&gt;lit-html@1.0&lt;/code&gt; that simplifies spreading multiple attributes/event listeners/properties/etc. onto an element. Before getting into really new features, I took a pass at updating these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spreading it on thick
&lt;/h3&gt;

&lt;p&gt;If you've worked with virtual DOM in the past you might be used to the ability to do something like &lt;code&gt;&amp;lt;Component {...props} /&amp;gt;&lt;/code&gt;, which is a powerful way to get an unknown number of properties applied to a component. A lot of talk around how and why to support this functionality when into &lt;a href="https://github.com/Polymer/lit-html/issues/923" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; and what came out allows you to do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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;lit-html&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;spread&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;@open-wc/lit-helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;div
      ...=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;spread&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-attribute&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;foo&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;?my-boolean-attribute&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.myProperty&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;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&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;@my-event&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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-event fired&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="s2"&gt;
    &amp;gt;&amp;lt;/div&amp;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;body&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;I'll admit to being a little reticent about the need to include sigils demarcating which type of value is being spread onto the element, but once you've been working with &lt;code&gt;lit-html&lt;/code&gt; for a while it starts to feel a little more normal.&lt;/p&gt;

&lt;p&gt;What's particularly at question here is the use of the &lt;code&gt;...&lt;/code&gt; "attribute" to bind the directive to the element. What is the &lt;code&gt;...&lt;/code&gt; attribute? Is it a property named &lt;code&gt;..&lt;/code&gt;? (Note the &lt;code&gt;.&lt;/code&gt; sigil demarcates a bound value should be applied as a property.) Is it magic syntax? No, it's a requirement of the v1.0 parser when binding directives to an element that &lt;em&gt;something&lt;/em&gt; be used to ensure associate to the elements and &lt;code&gt;...&lt;/code&gt; representing spread/destructuring in JS, it was included here in a question inducing way. Enter element expressions in the new releases and this is no longer needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&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;lit-element@next-major&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;spread&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;./spread.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;button
        &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;spread&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-attribute&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;foo&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;?my-boolean-attribute&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.myProperty&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;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&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;@my-event&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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-event fired&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;@click&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="o"&gt;=&amp;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;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;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;my-event&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="s2"&gt;
      &amp;gt;
        This button has a bunch of things spread on it.
      &amp;lt;/button&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MyElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beyond the ease of not needing a binding sigil, there isn't a whole lot of change in the usage here. Even in the implementation, there's not a whole lot of change to go from the functional to the class based code structure. You can see this running live in the browser/in code, here: &lt;a href="https://webcomponents.dev/edit/XugyS6YAQnEQXcS7YVKk" rel="noopener noreferrer"&gt;https://webcomponents.dev/edit/XugyS6YAQnEQXcS7YVKk&lt;/a&gt;. You can also take a closer look at the difference between the &lt;a href="https://github.com/open-wc/open-wc/blob/master/packages/lit-helpers/src/spread.js" rel="noopener noreferrer"&gt;v1.0&lt;/a&gt; and &lt;a href="https://webcomponents.dev/edit/collection/PfCT8IzVVjUxI3JiaF6x/XugyS6YAQnEQXcS7YVKk?file=src%2Fspread.ts" rel="noopener noreferrer"&gt;v2.0&lt;/a&gt; implementations.&lt;/p&gt;

&lt;p&gt;You'll see some of the cleanliness that class syntax brings to event listening generally. For instance, the ability to use the &lt;code&gt;eventHandler&lt;/code&gt; pattern to more simply distribute the events to appropriately bound methods. Look closer and you'll see the addition of the &lt;code&gt;connected&lt;/code&gt; and &lt;code&gt;disconnected&lt;/code&gt; methods to the &lt;code&gt;AsyncDirective&lt;/code&gt; base class leveraged therein. This allows the directive to clean up work that it's done while the part it relates to is not attached to the DOM. In this instance this allows us to add and remove event listeners when they are not needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The endless stream of time
&lt;/h3&gt;

&lt;p&gt;Some DOM events are built for a streaming form of listening by default (e.g. &lt;code&gt;pointerdown&lt;/code&gt; outlines the beginning of a stream of &lt;code&gt;pointermove&lt;/code&gt; events that end with a &lt;code&gt;pointerup&lt;/code&gt;) and make it really clear what the boundaries at both ends of the stream are. Some DOM events are not built this way (e.g. &lt;code&gt;input&lt;/code&gt; starts a stream of &lt;code&gt;input&lt;/code&gt; events that end of a &lt;code&gt;change&lt;/code&gt;) and need a little something extra to ensure they are consumed appropriately.&lt;/p&gt;

&lt;p&gt;In fact, streaming is so fun that you can say that again.&lt;/p&gt;

&lt;p&gt;Some DOM events are built for a steaming form of listening by default (e.g. a &lt;code&gt;change&lt;/code&gt; event marks the end of a stream of &lt;code&gt;input&lt;/code&gt; events that don't fire again until a new stream starts) and make it really clear what the boundaries at both ends of a stream are. Some DOM events are not built this way (e.g. &lt;code&gt;pointermove&lt;/code&gt; streams regardless of which side of a &lt;code&gt;pointerdown&lt;/code&gt; or &lt;code&gt;pointerup&lt;/code&gt; event you're on) and need a little something extra to ensure they are consumed appropriately.&lt;/p&gt;

&lt;p&gt;Whichever side of my mind I might be in agreement with in any given moment, I created the &lt;a href="https://webcomponents.dev/edit/ar6PrCzLTsv9wunVnifw" rel="noopener noreferrer"&gt;streaming listener directive&lt;/a&gt; to better support this reality. On top of maintaining the stateful progression of a stream, a streaming listener allows for binding fewer events at runtime by using the current state of the stream to determine what binding to do which can improve performance as well. Take a look at how this might be leveraged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;streamingListener&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;./streaming-listener&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;range&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;manage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;streamingListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&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="na"&gt;fn&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;start&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&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="na"&gt;fn&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;stream&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fn&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;end&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the directive supports the ability to bind &lt;code&gt;input&lt;/code&gt; events to both &lt;code&gt;this.start&lt;/code&gt; and &lt;code&gt;this.stream&lt;/code&gt; depending on the state of the stream. This allows for only a single event to be bound to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; at any one time without you needing to manage this (or any other state in regards to your event listening) locally increasing performance and reducing the chances of copy/paste centric bugs when leveraged across multiple contexts.&lt;/p&gt;

&lt;p&gt;While I've made some feature additions and API changes when going between the &lt;a href="https://webcomponents.dev/edit/ar6PrCzLTsv9wunVnifw?file=src%2Fstreaming-listener.ts" rel="noopener noreferrer"&gt;v1.0&lt;/a&gt; and &lt;a href="https://webcomponents.dev/edit/collection/PfCT8IzVVjUxI3JiaF6x/w9bQYjy7dvlMEKW4MDJY?file=src%2Fstreaming-listener.ts" rel="noopener noreferrer"&gt;v2.0&lt;/a&gt; implementations, the biggest benefit of the class syntax that I see is the ability to more directly keep the state necessary to empower the directive. Previously this was done through the use of the following &lt;code&gt;WeakMap&lt;/code&gt;s:&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;previousValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fn&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fn&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fn&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nl"&gt;removeEventListeners&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="k"&gt;void&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stateMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&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;With these hanging around in the module scope, we are able to take advantage of the idea that the &lt;code&gt;Part&lt;/code&gt; representing the location of the directive in the template is an object that keeps identity across multiple renders, which allows us access to stored state on subsequent render passes. However, this can feel a little magic... why is this &lt;code&gt;Part&lt;/code&gt; always the same? Can I really rely on that? Why did I make &lt;code&gt;previousValues&lt;/code&gt; and &lt;code&gt;stateMap&lt;/code&gt; separate? Oh, wait, that's not about magic, that's just me code reviewing myself...&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;lit-html@2.0&lt;/code&gt; version, we can avoid these questions altogether by leveraging the class syntax to do exactly what classes are meant to do, keep state. We also leverage some nice defaults in our directive arguments to make it easy to apply the directive not only for events streaming between a "start" and "stop" event but also as an on/off listener for enter/leave style events as well as to stream events (like &lt;code&gt;pointermove&lt;/code&gt;) on on the outside (or between "stop" and "start") of our stream:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;streamingListener&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointerdown&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;start&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;streamInside&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointermove&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;streamInside&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointerup&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;end&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;streamOutside&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pointermove&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;streamOutside&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;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/canvas&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This really takes the streaming listener directive to a whole other level, all with only the smallest amount of additional code, and a clearer API both internally and externally.&lt;/p&gt;

&lt;p&gt;Seeing what it looks like to update places I've been, I was even more excited to see where these new APIs might be able to take us with new possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Element expressions
&lt;/h2&gt;

&lt;p&gt;In both of the above examples, we were able to remove extraneous binding locations thanks to "element expressions" that allow you to bind a directive directly to the element that it is applied to, rather than a specific part that you've outlined with an "attribute". For the spread directing that reduced &lt;code&gt;&amp;lt;div ...=${spread({...})&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;div ${spread({...})&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;div @manage=${streamingListener({...},{...},{...})}&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;div ${streamingListener({...})}&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;, a win for brevity and clarity. Using this feature, the &lt;code&gt;ref()&lt;/code&gt; directive was added to the &lt;code&gt;lit-html&lt;/code&gt; built-ins giving us the ability to cache a reference to an element as it is rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&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="nx"&gt;html&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;lit-html&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;createRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&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;lit-html/directives/ref.js&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;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;input &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; /&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;inputRef&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="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This greatly reduces the work need to get a reference to an element when using &lt;code&gt;lit-html&lt;/code&gt; alone, and, whether using &lt;code&gt;lit-html&lt;/code&gt; directly or as part of &lt;code&gt;LitElement&lt;/code&gt;, prevents the need to query the element again after rendering. Take a &lt;a href="https://webcomponents.dev/edit/collection/PfCT8IzVVjUxI3JiaF6x/tpjqACEiKS6YF4LV3lJs" rel="noopener noreferrer"&gt;test drive&lt;/a&gt; of the &lt;code&gt;ref()&lt;/code&gt; directive in this &lt;code&gt;lit-html&lt;/code&gt; only demo. I see this as a great feature for leveraging &lt;code&gt;lit-html&lt;/code&gt; in something like StorybookJS where you will be working with pre-built custom elements and no wanting to make a new wrapping element or strange workaround to have access to elements post-render. But, what element expressions really make available are things like:&lt;/p&gt;


&lt;blockquote&gt;
&lt;p&gt;🤯 &lt;a href="https://t.co/Ty1x9FkSpT" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/Ty1x9FkSpT" rel="noopener noreferrer"&gt;https://t.co/Ty1x9FkSpT&lt;/a&gt;&lt;/p&gt;— Westbrook (@WestbrookJ) &lt;a href="https://twitter.com/WestbrookJ/status/1341558826101846016?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;December 23, 2020&lt;/a&gt;
&lt;/blockquote&gt; 
&lt;h2&gt;
  
  
  Let's do a FLIP
&lt;/h2&gt;

&lt;p&gt;First, what is &lt;a href="https://aerotwist.com/blog/flip-your-animations/" rel="noopener noreferrer"&gt;FLIP&lt;/a&gt;? Paul Lewis says it best, so definitely check out his blog, but the short story is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set the (F)irst frame of your animation and cache the values you're looking to animate&lt;/li&gt;
&lt;li&gt;set the (L)ast frame of your animation and cache the target values again&lt;/li&gt;
&lt;li&gt;apply the (I)nverted values of those properties to the end frame&lt;/li&gt;
&lt;li&gt;and then (P)lay the animation by removing them with a &lt;code&gt;transition&lt;/code&gt; applied&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works best with things that can be applied as &lt;code&gt;transforms&lt;/code&gt; or &lt;code&gt;opacity&lt;/code&gt;, as they can be rendered on the GPU for maximum performance.&lt;/p&gt;

&lt;p&gt;Generally, the tricky parts are doing the work between the first and last frames (but this is simplified by a multi-pass render as the first frame will simply be the previous render and the last frame will be the current render) and then calculating the inverted values on the element. In the example that we are about to borrow from the &lt;a href="https://svelte.dev/tutorial/animate" rel="noopener noreferrer"&gt;Svelte documentation&lt;/a&gt; we'll be focusing specifically on position properties which will allow us to keep that math a little more contained.&lt;/p&gt;
&lt;h2&gt;
  
  
  Or, rather, a &lt;code&gt;${flip()}&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;${flip()}&lt;/code&gt; loosely referenced by Justin Fagnani in the above tweet theorized a list of items that when rearranged uses a "FLIP" algorithm to ensure that the motion between one place in the list and the next is smoothly animated. In the Svelte example, not only are there two lists, but you can remove items from those lists, which is where the real fun starts. (disclaimer: maybe we have different definitions of "fun"...)&lt;/p&gt;

&lt;p&gt;Before we get deeper into how it works, take a look at the &lt;a href="https://webcomponents.dev/edit/collection/PfCT8IzVVjUxI3JiaF6x/9hcibsyvtFA7tWwRj8gL" rel="noopener noreferrer"&gt;code in practice&lt;/a&gt;. Like most to-do apps (and &lt;a href="https://dev.to/westbrook/litelement-to-do-app-4ngn"&gt;I've made&lt;/a&gt; &lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;a few&lt;/a&gt;...haven't we all?), you're able to add an item, mark the item as "done" (or not), and delete the item. Adding will automatically append the item to the "todo" list. Clicking an item will toggle it between "todo" and "done", which will cause it to animate between the to lists and the remaining items in its original list to animate to fill the space the toggled item previously took up. Using the "delete" button will fade the item into the background while the remaining items smoothly fill up the previously used space. Try it out, do weird stuff, report bugs!&lt;/p&gt;
&lt;h3&gt;
  
  
  How's it work?
&lt;/h3&gt;

&lt;p&gt;Taking the code pretty straight out of the above Tweet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;repeat&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;items&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;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;` &amp;lt;li &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/li&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;repeat()&lt;/code&gt; directive built-in to &lt;code&gt;lit-html&lt;/code&gt; allows you to loop over an array of items and then the optional &lt;code&gt;id&lt;/code&gt; argument is passed (here we see it as &lt;code&gt;i =&amp;gt; i.id&lt;/code&gt;) the directive will maintain a single template instance for each item. This means that the instance of the &lt;code&gt;flip()&lt;/code&gt; directive in each item will be the same regardless of where the item appears in the array order and we'll be able to cache the position of the item in the page from one render to the next. You'll see this in the code where we save the value returned by &lt;code&gt;getBoundingClientRect()&lt;/code&gt; on the &lt;code&gt;boundingRect&lt;/code&gt; property of the directive class. This way we can easily use that cached value to determine our "first" frame. We then wait for the &lt;code&gt;Promise.resolve().then()&lt;/code&gt; timing (the timing on which &lt;a href="https://dev.to/thepassle/litelement-a-deepdive-into-batched-updates-3hh"&gt;&lt;code&gt;LitElement&lt;/code&gt; batches its updates&lt;/a&gt;) to capture the "last" frame of our animation. We then take the delta so we can "invert" the values before "playing" the animation via the CSS &lt;code&gt;transition&lt;/code&gt; property.&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="nf"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;firstStyleMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;lastStyleMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;string&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;event&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&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="nx"&gt;removing&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;const&lt;/span&gt; &lt;span class="nx"&gt;previous&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;boundingRect&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;boundingRect&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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&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;deltaX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&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;boundingRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&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;deltaY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&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;boundingRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&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;deltaX&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;removing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredListener&lt;/span&gt; &lt;span class="o"&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="nx"&gt;TransitionEvent&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;if &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;target&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;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transitionend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredListener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;transitionend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredListener&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;translate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`translate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deltaX&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deltaY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyStyles&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;firstStyleMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;firstStyleMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&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="s2"&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;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="s2"&gt;`transform &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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms &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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timingFunction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyStyles&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;lastStyleMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;removing&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; `&lt;/span&gt; &lt;span class="p"&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;lastStyleMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&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="s2"&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;With that, all of the repositioning within a single list works like a dream. But, you may remember that in the Svelte demo we're recreating there actually are two different lists that elements animate between, as well as an animation that occurs when an element is removed from all lists, and if you do you may already be seeing where things need to get tricky.&lt;/p&gt;

&lt;h3&gt;
  
  
  When items are the same but not the same...
&lt;/h3&gt;

&lt;p&gt;While the &lt;code&gt;repeat()&lt;/code&gt; directive is great for associating an item to a DOM template within a single instance, it doesn't currently do this across multiple instances. This means that the DOM for a "todo" item and a "done" item with the same ID will not actually be the same and, what's worse, nor will the &lt;code&gt;flip()&lt;/code&gt; directive that manages that DOM. To support this context, we &lt;em&gt;will&lt;/em&gt; be needing a manage a little bit of state outside of our directive class and to do so you'll see &lt;code&gt;const disconnectedRects = new Map();&lt;/code&gt;, where we will cache the position values of elements from directives that have been disconnected from the DOM. To power this approach, we'll also add an optional &lt;code&gt;id&lt;/code&gt; to our directive's properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;repeat&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;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;label &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&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="s2"&gt;&amp;gt;
      &amp;lt;input
        type=checkbox
        ?checked=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        @change=&lt;/span&gt;&lt;span class="p"&gt;${()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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="s2"&gt;
      &amp;gt;
      &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;todo&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="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;button
        @click=&lt;/span&gt;&lt;span class="p"&gt;${()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
        class="button"
      &amp;gt;remove&amp;lt;/button&amp;gt;
    &amp;lt;/label&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;With this id cached on to our directive class and the &lt;code&gt;disconnected()&lt;/code&gt; that we learned about above, we'll be able to store the position of our element in a place where the next directive of the same id can find it. Here you'll see how a directive without a value for &lt;code&gt;boundingRect&lt;/code&gt; will first check to see if there &lt;em&gt;was&lt;/em&gt; a rect for its id before generating a new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingRect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;disconnectedRects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;disconnectedRects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;disconnectedRects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&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;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the "new" instance of that directive to use the last position of the "old" instance for the "first" frame of its ensuing animation, which makes it appear as if the item is animating from one list to the next. Here we also denote that the item is no longer "disconnected" by removing its rect from the &lt;code&gt;disconnectedRects&lt;/code&gt; cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  When are the items not there at all?
&lt;/h3&gt;

&lt;p&gt;Our items now animate with a list and between lists, but when an item is deleted, it's gone. What do we do then? This is where is good to know about your &lt;a href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/" rel="noopener noreferrer"&gt;Tasks, microtasks, queues and Schedules&lt;/a&gt; in javascript. Go ahead and get your read on, I'll wait.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;LitElement&lt;/code&gt;, as we learned earlier, updates are batched in &lt;code&gt;Promise.resolve().then()&lt;/code&gt; (or microtask, at the end of the current task) time. In a standard animation, particularly one that FLIPs, you'll do work in &lt;code&gt;requestAnimationFrame()&lt;/code&gt; (&lt;code&gt;rAF()&lt;/code&gt;) time (or just before the &lt;em&gt;next&lt;/em&gt; frame). We can use this to empower our "delete" animation.&lt;/p&gt;

&lt;p&gt;Above we learned about some housekeeping that we were doing in microtask time: &lt;code&gt;disconnectedRects.delete(this.id)&lt;/code&gt;. This is run when a directive is new and has possibly just pulled this rect out of the cache for use in a subsequent animation. However, when an item is deleted there will be no new items with the same id, and this cache will not be cleaned up. This means that in &lt;code&gt;rAF()&lt;/code&gt; time this rect will still be in the cache and we can add the following to our &lt;code&gt;disconnected()&lt;/code&gt;:&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="nf"&gt;requestAnimationFrame&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;disconnectedRects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&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;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&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 means that the position data saved in the directive can serve as the "first" frame of our "delete" animation and by appending the cached element (which is no longer on the DOM due to the previously completed render pass) to the previously cached parent, we can trigger the "delete" animation as follows:&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="nf"&gt;remove&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;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-1&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;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;scale(0.5)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.5&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="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;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;disconnectedRects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&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;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, we have our complete animated todo list with the single addition of a &lt;code&gt;${flip({id})}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When your users aren't ready to do a &lt;code&gt;${flip()}&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Recently, we've seen a rise in user preference media queries on the web. You &lt;em&gt;may&lt;/em&gt; be taking advantage of one right now; &lt;code&gt;@media (prefers-color-scheme: dark)&lt;/code&gt; gets a lot of play in the development community. However, there is a growing number of &lt;code&gt;prefers-*&lt;/code&gt; media queries to take advantage of in the development of our products, and doing so can be not just that extra polish on the work we're doing, but the difference between certain visitors being able to enjoy your work or not. On top of &lt;code&gt;prefers-color-scheme&lt;/code&gt;, &lt;code&gt;prefers-contrast&lt;/code&gt; can mark the difference between whether someone with visual disabilities can consume your content. In locations of connectivity or high data cost, &lt;code&gt;prefers-reduced-data&lt;/code&gt; can increase the amount of your content someone might be able to consume. In the case of content featuring motion, or rather content that &lt;code&gt;${flip()}&lt;/code&gt;s, the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; query can support preparing your content to take into account its effect on your audiences health. &lt;a href="https://twitter.com/TatianaTMac" rel="noopener noreferrer"&gt;Tatiana Mac&lt;/a&gt; goes into great detail on how you can bring &lt;code&gt;prefers-reduced-motion&lt;/code&gt; into the conversation as part of the development of our products and proposes &lt;a href="https://tatianamac.com/posts/prefers-reduced-motion/" rel="noopener noreferrer"&gt;"Taking a no-motion-first approach to animations"&lt;/a&gt;. I think she's outlined an excellent path forward for our application of animation in a product, so I've made it a default of the &lt;code&gt;${flip()}&lt;/code&gt; directive as follows.&lt;/p&gt;

&lt;p&gt;In javascript, we can access the current state of a media query via &lt;code&gt;window.matchMedia(queryGoesHereAsAString).matches&lt;/code&gt;. In the case of a no-motion-first animation, we can cache a single match media object as follows:&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;hasNoMotionPreference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-reduced-motion: no-preference)&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;From there we can leverage whether or not the query matches to gate the initiation of animation in our experience. Currently, we do this in both the &lt;code&gt;update()&lt;/code&gt; and &lt;code&gt;disconnected()&lt;/code&gt; lifecycle methods. For &lt;code&gt;disconnected()&lt;/code&gt;, we can simply gate all of the functionality therein, like so:&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="nf"&gt;disconnected&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;hasNoMotionPreference&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... animation work done when there is `no-preference`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;updated()&lt;/code&gt; we don't want to be so blunt. This is to prepare for the possibility that the preference changes over the course of the experience. To do so we want to complete all the administrative work of caching and measuring the elements in question, which serves to prepare them to animate at any later time, and then gate the actual initiation of the current animation. In this way only the call to &lt;code&gt;prepareToFlip()&lt;/code&gt; should be gated:&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="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &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;span class="p"&gt;{}]:&lt;/span&gt; &lt;span class="nx"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;render&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... administrative work of caching the element&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;hasNoMotionPreference&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="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// exit early when there is `no-preference`&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepareToFlip&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, our elements only &lt;code&gt;${flip()}&lt;/code&gt; when a browser can make known the &lt;code&gt;no-preference&lt;/code&gt; state of this preference, which means we're both delivering this experience as a no-motion-first animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else does it do?
&lt;/h2&gt;

&lt;p&gt;You'll notice that the settings for &lt;code&gt;flip()&lt;/code&gt; also takes an &lt;code&gt;options&lt;/code&gt; parameter. This surfaces the ability to customize the transitions via the following &lt;code&gt;Options&lt;/code&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;timingFunction&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playing with this I discovered that there's a &lt;code&gt;step()&lt;/code&gt; function available in the CSS &lt;code&gt;transition-timing-function&lt;/code&gt; which is super cool. The only problem is that &lt;code&gt;step(6, end)&lt;/code&gt; causes the animation to look like it's running at about two frames per second (e.g. not buttery smooth) if you aren't prepared for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else could it do?
&lt;/h2&gt;

&lt;p&gt;While I noticed that my &lt;code&gt;LitElement&lt;/code&gt; implementation of this interface came in right around the same number of lines of code as the notoriously terse Svelte did (give or take some TS definitions), I do realize that the original version leverages the ability to customize the "delete" animation from the outside. My example does not currently do this. It doesn't currently allow for any special customization of any of the animations. However, these animations are powered is pseudo &lt;a href="https://lit-html.polymer-project.org/guide/template-reference#stylemap" rel="noopener noreferrer"&gt;&lt;code&gt;styleMap&lt;/code&gt;&lt;/a&gt; objects and as such could be passed additional properties to animate. This would allow consumers to even more finely tune the animation you get between renders and could open some really fun paths in the future. It's important to remember (as we salivate over the possibility) which CSS properties can be &lt;a href="https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/" rel="noopener noreferrer"&gt;performantly animated&lt;/a&gt;. In this way, maybe the right level of power would be to and options for &lt;code&gt;opacity&lt;/code&gt; and &lt;code&gt;scale&lt;/code&gt; (possibly as an opt-in that worked with width/height from the rect internally) so as to ensure users ship high-quality experiences.&lt;/p&gt;

&lt;p&gt;One pattern that I've enjoyed recently that could be built onto this is the surface the sizing deltas a CSS Custom Properties to be consumed across a number of CSS properties via &lt;code&gt;calc()&lt;/code&gt;. I originally discovered this technique in this great &lt;a href="https://www.youtube.com/watch?v=SXtFBXmwgLQ&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;Keyframers tutorial&lt;/a&gt; and then later expanded on it with the help of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSS/RegisterProperty" rel="noopener noreferrer"&gt;Hounini's &lt;code&gt;CSS.registerProperty&lt;/code&gt;&lt;/a&gt; currently available in Blink based browsers to be even more buttery smooth by helping it even more &lt;a href="https://codepen.io/Westbrook/pen/vYNJjyg" rel="noopener noreferrer"&gt;correctly handle the scaling of animating surfaces with rounded corners&lt;/a&gt;. I'll save this sort of advanced application for after the &lt;code&gt;lit-*&lt;/code&gt; releases go stable, however.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do you think?
&lt;/h2&gt;

&lt;p&gt;Is this a cool evolution of the &lt;code&gt;lit-html&lt;/code&gt; and &lt;code&gt;LitElement&lt;/code&gt; ecosystem? Does it make you excited for the pending stable release? Can you already imagine the great things you'd like to build with it?&lt;/p&gt;

&lt;p&gt;Tell me all about it!&lt;/p&gt;

&lt;p&gt;Building for the web is all that much more exciting when we're doing it together, so I hope you'll share your thoughts on these new APIs and how I've leveraged them for good or naught I know it helps me make better code, and hopefully, it does the same for you (or there next reader that visits).&lt;/p&gt;




&lt;p&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@arstyy?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Austin Neill&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/flip?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

</description>
      <category>lithtml</category>
      <category>javascript</category>
      <category>animation</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Mind the `document.activeElement`!</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Wed, 19 Feb 2020 00:35:51 +0000</pubDate>
      <link>https://dev.to/open-wc/mind-the-document-activeelement-2o9a</link>
      <guid>https://dev.to/open-wc/mind-the-document-activeelement-2o9a</guid>
      <description>&lt;p&gt;The element that currently has &lt;em&gt;focus&lt;/em&gt; in your HTML at any point in time can be accessed as &lt;code&gt;document.activeElement&lt;/code&gt;. If you don't know, now you know!&lt;/p&gt;

&lt;p&gt;What's more, while it can be difficult to capture the value of this property while debugging, at least without changing it, you can leverage browsers that allow you to &lt;a href="https://developers.google.com/web/tools/chrome-devtools/console/live-expressions" rel="noopener noreferrer"&gt;"watch live expressions"&lt;/a&gt; to keep the current value of this property available at all times, 😱. No, really, go check it out right now!&lt;/p&gt;

&lt;p&gt;There are lots of ways you can leverage this in your work, whether in functional code, unit tests or debugging, but I'm not looking to walk you through all the things that should be, can be, or will be in this area. However, if you're already using this value, I'd love to hear more about it in the comments. My usage can definitely be super-powered by hearing great workflows from others, so I look forward to hearing what you've got up your sleeves.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6l89soeupyn2bdpudjau.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6l89soeupyn2bdpudjau.jpg" alt="This isn't the  raw `document` endraw  you're looking for..."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are gathered here, today, to go a little bit deeper on what &lt;code&gt;document&lt;/code&gt; means and when &lt;em&gt;the&lt;/em&gt; &lt;code&gt;document&lt;/code&gt; isn't the “document”0 you're looking for and what to do in that case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out of the shadows a new &lt;code&gt;document&lt;/code&gt; rises...
&lt;/h2&gt;

&lt;p&gt;Do you find yourself using code like the following to attach a shadow root to elements in your application?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Do you find yourself attaching that shadow root to custom elements that you've defined?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomElement&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;customElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CustomElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then you're already using web components.&lt;/p&gt;

&lt;p&gt;If not, I highly recommend them in many and varied use cases! The benefits I've gained from working with custom elements and shadow DOM from well before both APIs were even supported by two browsers, &lt;a href="https://twitter.com/polymer/status/1217578939456970754" rel="noopener noreferrer"&gt;let alone all of them&lt;/a&gt;, are all positive, and the full possibilities of this sometimes wholely different paradigm of client-side development are still only beginning to be fully explored.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1217578939456970754-367" src="https://platform.twitter.com/embed/Tweet.html?id=1217578939456970754"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1217578939456970754-367');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1217578939456970754&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;If you're ready to start exploring them too, check out &lt;a href="https://dev.to/thepassle/web-components-from-zero-to-hero-4n4m"&gt;Web Components: from zero to hero&lt;/a&gt;, an amazing introduction to these technologies by &lt;a href="https://dev.to/thepassle"&gt;Pascal Schilp&lt;/a&gt;, and you'll be well on the way.&lt;/p&gt;

&lt;p&gt;When creating your own custom element with their own shadow roots, you're getting a "DOM subtree that is rendered separately from a document's main DOM tree". A subtree that is separate from the &lt;code&gt;document&lt;/code&gt;: a &lt;code&gt;document&lt;/code&gt; to itself. Inside of that subtree, you get encapsulation for whatever DOM lives therein from external selectors, a special HTML &lt;code&gt;slot&lt;/code&gt; API for composing DOM from the outside of the element, and much more. However, when minding the &lt;code&gt;document.activeElement&lt;/code&gt;, it is important to look a little deeper at the specific cost that we pay to get these new capabilities.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document.activeElement&lt;/code&gt; points to the element in the &lt;code&gt;document&lt;/code&gt; that currently has &lt;em&gt;focus&lt;/em&gt;, but what happens when that element isn't actually in the &lt;code&gt;document&lt;/code&gt;? If your shadow DOM has focusable elements internal to it, and one of those elements currently has &lt;em&gt;focus&lt;/em&gt;, &lt;code&gt;document.activeElement&lt;/code&gt; (like all other selectors) will not be able to point directly to it. What it will point to is the first element in the &lt;code&gt;document&lt;/code&gt; that includes a shadow DOM. So, taking the following tree into account:&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;document&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-element&amp;gt;&lt;/span&gt;
      #shadow-root
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Sub-title&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;other-custom-element&amp;gt;&lt;/span&gt;
          #shadow-root
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- The link _has_ focus --&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element above is focused and &lt;code&gt;document.activeElement&lt;/code&gt; is referenced, the value returned will point to the &lt;code&gt;&amp;lt;custom-element&amp;gt;&lt;/code&gt; just below the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;; not the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, not the &lt;code&gt;&amp;lt;other-custom-element&amp;gt;&lt;/code&gt; that is its parent, and likely, not what you expected. &lt;/p&gt;

&lt;h2&gt;
  
  
  A brave new world
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Oh no, shadow DOM broke the internet!&lt;br&gt;
&lt;cite&gt;- alarmist JS (framework) user&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, in a word, "no".&lt;/p&gt;

&lt;p&gt;With more nuance... shadow DOM has broken the assumption that the specifics of &lt;em&gt;focus&lt;/em&gt; in any one component will bleed into all other components, so yes the fragile, fly by night, shoot from the hip internet that was previously the only option available to use is &lt;em&gt;broken&lt;/em&gt; if you choose to use shadow DOM and the shadow boundaries that they create. However, if you choose to use shadow DOM and the shadow boundaries that they create, you now have access to a more nuanced, controllable, and refined DOM than ever before. Yes, some things that you may have taken for granted in the past may be a little different than you remember, but you also have access to capabilities that were previously impossible or prohibitively complex.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But... if I can't see what the currently focused element is, what &lt;em&gt;will&lt;/em&gt; I do?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While inside a shadow root, &lt;code&gt;document.activeElement&lt;/code&gt; will not allow you to see if any other elements in the subtree are currently focused, yes. However, from the root of a subtree, we now have &lt;code&gt;shadowRoot.activeElement&lt;/code&gt; available to us when we desire to find the focused element in our current subtree. This means that instead of having to worry about the entire document (both above and below your current component), you can take into consideration only the DOM belonging to the subtree related to the current component.&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, how do I leverage this?
&lt;/h2&gt;

&lt;p&gt;I feel you start to think, "ok, that sounds like I could find a way to process this as being cool after ruminating on it for a while, but how do I figure out what shadow root I'm in?", and that's a great question! The answer is in the &lt;code&gt;getRootNode()&lt;/code&gt; method that has been added to &lt;code&gt;Element&lt;/code&gt; as part of the introduction of shadow DOM. With this method, you will be given the root of the DOM tree in which the element you called &lt;code&gt;getRootNode()&lt;/code&gt; on lives. Whether what is returned is the actual &lt;code&gt;document&lt;/code&gt; or an individual &lt;code&gt;shadowRoot&lt;/code&gt; its member property &lt;code&gt;activeElement&lt;/code&gt; will allow you to know what element in that tree is currently focused.&lt;/p&gt;

&lt;p&gt;Let's revisit our sample document from above to better understand what this means...&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;document&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;custom-element&amp;gt;&lt;/span&gt;
      #shadow-root
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Sub-title&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;other-custom-element&amp;gt;&lt;/span&gt;
          #shadow-root
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- The link _has_ focus --&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When you have a reference to the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element therein:&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRootNode&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="nf"&gt;log&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="c1"&gt;// otherCustomElement#shadowRoot&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&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;activeElement&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// &amp;lt;a href="#"&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When you have a reference to the &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; element therein:&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRootNode&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="nf"&gt;log&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="c1"&gt;// customElement#shadowRoot&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&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;activeElement&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// &amp;lt;other-custom-element&amp;gt;&amp;lt;/other-custom-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And, when you have a reference to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element therein:&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRootNode&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="nf"&gt;log&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="c1"&gt;// document&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&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;activeElement&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// &amp;lt;custom-element&amp;gt;&amp;lt;/custom-element&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  But, a component &lt;em&gt;should&lt;/em&gt; have some control of its children, right?
&lt;/h2&gt;

&lt;p&gt;I completely agree! But, in the context of a free and single &lt;code&gt;document&lt;/code&gt; "some" control becomes &lt;em&gt;complete and total&lt;/em&gt; control.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr4krolxrj2ncaiigf02p.jpg" 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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr4krolxrj2ncaiigf02p.jpg" alt="Not just "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the case of shadow DOM encapsulated subtrees, the control that a parent has over its children is only the control that said child offers in the form of its public API. If you don't want to cede any control to a parent element implementing your custom element, you do not have to. Much like the first night you stayed out past curfew, this will surprise most parents accustomed to a level of control they maybe never should have had. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Will they get the number to your new cell phone?&lt;/li&gt;
&lt;li&gt;Will you pick up when they call?&lt;/li&gt;
&lt;li&gt;Will you still come home for dinner on Sunday nights?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these questions and more are yours to answer via the attributes, properties, and methods that your elements surface to the public. Take care to respect your parents, but don't think that you have to become a doctor/lawyer/the President just because your mother said you should.&lt;/p&gt;
&lt;h2&gt;
  
  
  The components are alright
&lt;/h2&gt;

&lt;p&gt;In this way, we might address the following simplification of the DOM we've reviewed through much of this article:&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;document&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;other-custom-element&amp;gt;&lt;/span&gt;
      #shadow-root
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a link&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- The link _has_ focus --&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;When accessing &lt;code&gt;document.activeElement&lt;/code&gt; from the outside, again we will be returned &lt;code&gt;other-custom-element&lt;/code&gt; in reverence of the constrained control we now have over our once singular &lt;code&gt;document&lt;/code&gt;. In this context, we may want to forward a &lt;code&gt;click&lt;/code&gt; event into our focused element, however not having direct access to the anchor tag through the shadow boundary, we'd be calling &lt;code&gt;click()&lt;/code&gt; on &lt;code&gt;other-custom-element&lt;/code&gt;. By default, this type of interaction on the shadow DOM of &lt;code&gt;other-custom-element&lt;/code&gt; would be prevented. In the case that we wanted this sort of thing to be possible, we could build the following extension of the &lt;code&gt;click()&lt;/code&gt; method into our &lt;code&gt;other-custom-element&lt;/code&gt; element to pass the &lt;code&gt;click&lt;/code&gt; into its child:&lt;/p&gt;

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

click() {
  this.shadowRoot.querySelector('a').click();
}


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

&lt;/div&gt;

&lt;p&gt;But what about the case where there are more than one anchor tags inside of an &lt;code&gt;other-custom-element&lt;/code&gt;?&lt;/p&gt;

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

&amp;lt;other-custom-element&amp;gt;
  #shadow-root
    &amp;lt;a href="#"&amp;gt;This is a link&amp;lt;/a&amp;gt;
    &amp;lt;a href="#"&amp;gt;This is also a link&amp;lt;/a&amp;gt; &amp;lt;!-- The link _has_ focus --&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;In this case, we can take advantage of the &lt;code&gt;activeElement&lt;/code&gt; accessor on a shadow root and target the correct anchor tag as follows to make an even more flexible custom element implementation:&lt;/p&gt;

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

click() {
  this.shadowRoot.activeElement.click();
}


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

&lt;/div&gt;

&lt;p&gt;From here, there are any number of next steps that you can take to produce your own powerful custom elements that leverage the encapsulation offered by the shadow DOM to structure more nuanced, yet eminently powerful APIs to surface to users of your components. As you find patterns that work well for you, I'd love to hear about them in the comments below. If you're interested in uses of the &lt;code&gt;activeElement&lt;/code&gt; property in the wild, I invite you to checkout &lt;a href="https://opensource.adobe.com/spectrum-web-components/" rel="noopener noreferrer"&gt;Spectrum Web Components&lt;/a&gt; where we are actively reviewing the use of this and many other practices to power our growing web component implementation of the &lt;a href="https://spectrum.adobe.com/" rel="noopener noreferrer"&gt;Spectrum&lt;/a&gt;, Abode's design system.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>shadowdom</category>
      <category>a11y</category>
      <category>html</category>
    </item>
    <item>
      <title>Things We did for the First Time in 2019...</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Tue, 31 Dec 2019 02:34:11 +0000</pubDate>
      <link>https://dev.to/westbrook/things-i-did-for-the-first-time-in-2019-2980</link>
      <guid>https://dev.to/westbrook/things-i-did-for-the-first-time-in-2019-2980</guid>
      <description>&lt;p&gt;Hi, I'm Westbrook and I'm a front-end engineer. &lt;/p&gt;

&lt;p&gt;👋🏼&lt;/p&gt;

&lt;p&gt;I've been doing this CSS/HTML/JS thing for more than 12 years now, and I'm always amazed by all of the interesting technologies that come and go in our industry. Some things have been with us forever, others have just joined the party but seem destined to be with us for a while, and many more enter and leave the picture as fast as you can turn your head to learn what they are. Often this only really comes clear to me by way of trending topics on Twitter or the blogosphere. Particularly, in the way that my career has worked out, my work with the web fits into a couple of neat little epochs: 3 or 4 years of predominately CSS/HTML development, 3 or 4 years of working with jQuery/Backbone.js derivatives, and 5 or 6 years of building component/design systems and applications with web components. However, this year was a little different. While working to discover the right team with which to continue to develop my career, I ended up changing jobs twice, and across those three different teams, I encountered a lot of things that might not be new to the world of front-end engineering but were certainly new to me.&lt;/p&gt;

&lt;p&gt;While reviewing them for myself, I was surprised how many things I had seen in the community for years but only just this year ended up working with myself. In that it surprised me, I thought it might surprise or be of interest to others, especially those of us that find it hard to keep up with all of the great concepts and technologies that are consistently making their way around the front-end engineering community. This will also be a good chance for me to hear what some of the rest of your might have done for the first time this year in your work as well! I'm sure there be lots of things I find there that end up being firsts for me in 2020...&lt;/p&gt;

&lt;p&gt;I'll get started in no particular order here, you share yours in the comments below!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;worked at three different companies&lt;/li&gt;
&lt;li&gt;had a job interview in San Francisco &lt;/li&gt;
&lt;li&gt;published on &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;handed over management of an entire product in just 2 weeks&lt;/li&gt;
&lt;li&gt;realized that no amount of documentation is enough&lt;/li&gt;
&lt;li&gt;worked full-time with teammates in other countries&lt;/li&gt;
&lt;li&gt;worked in an office that wasn't the "main" location of my team&lt;/li&gt;
&lt;li&gt;built a mono-repo with Lerna&lt;/li&gt;
&lt;li&gt;developed React for production&lt;/li&gt;
&lt;li&gt;shipped an application built with webpack&lt;/li&gt;
&lt;li&gt;wrote Jest tests&lt;/li&gt;
&lt;li&gt;shipped CSS Grid-based UIs to production&lt;/li&gt;
&lt;li&gt;managed state with Redux&lt;/li&gt;
&lt;li&gt;joined an open-source project as a maintainer&lt;/li&gt;
&lt;li&gt;published packages to public and private NPM registries&lt;/li&gt;
&lt;li&gt;worked with a mono-repo built with Rush&lt;/li&gt;
&lt;li&gt;used Artifactory&lt;/li&gt;
&lt;li&gt;managed state with finite state machines&lt;/li&gt;
&lt;li&gt;used &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;used &lt;a href="https://webcomponents.dev/" rel="noopener noreferrer"&gt;WebComponents.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;developed in Vue.js&lt;/li&gt;
&lt;li&gt;open-sourced work I'd done for my company&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's likely more that I've missed, but this is already a pretty long list of firsts! I'm going to take it as a good sign that no matter how experienced we become in our fields that there's always value in keeping an open mind and experimenting with new and different techniques...&lt;/p&gt;

&lt;p&gt;What's your list look like?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>2019</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>`composed: true` considered harmful?</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Thu, 07 Nov 2019 20:05:01 +0000</pubDate>
      <link>https://dev.to/open-wc/composed-true-considered-harmful-5g59</link>
      <guid>https://dev.to/open-wc/composed-true-considered-harmful-5g59</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: it was brought to my attention that in my desire to strike a very click-bait-like pose in reference to a wide field of "considered harmful" articles, that it might too directly call to mind a &lt;a href="https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html" rel="noopener noreferrer"&gt;seminal post&lt;/a&gt; in this regard that takes a clear stance in opposition to its subject. While it is absolutely my goal to trick you onto this page with such a title, I won't pretend to have THE definitive stance on almost any subject, let alone on one as rich and varied as event handling. I do hope to strike up a good dialog if you'll join me in one, and find that a shared foundation of knowledge is the best place to get started on one. So, let's begin!&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;
Photo by &lt;a href="https://unsplash.com/@heftiba" rel="noopener noreferrer"&gt;Toa Heftiba&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/event" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;First of all, what even is &lt;code&gt;composed: true&lt;/code&gt;, and when &lt;em&gt;might&lt;/em&gt; you use it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/composed" rel="noopener noreferrer"&gt;&lt;code&gt;Event.composed&lt;/code&gt;&lt;/a&gt; outlines whether a DOM event will cross between the shadow DOM in which the event is dispatched into the light DOM in which the element that the shadow root is attached to exists. As you'll find in the MDN article on the subject, "all UA-dispatched UI events are composed" by default, but when you work with manually dispatched events you have the opportunity to set the value for this property as you see fit. So the "what" of &lt;code&gt;composed: true&lt;/code&gt; at its simplest is "a way to manage the encapsulation of your event transmission", and the "when" is namely "while working with shadow DOM", a practice that is not exclusive to but has become somewhat synonymous to working with web components; shadow DOM, custom elements, ES6 modules, and the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element. Next, we'll review some important concepts before we try to come to a decision about &lt;code&gt;composed: true&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native DOM events and how they work&lt;/li&gt;
&lt;li&gt;Manually dispatched events and their configurations/extensions&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;detail&lt;/code&gt;s on Custom Events&lt;/li&gt;
&lt;li&gt;The world of events within a shadow root&lt;/li&gt;
&lt;li&gt;Composed Events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, we'll all be specialists and we can get into some practices and patterns with DOM events that might be useful in your applications. I'll share some ideas that I've had or used, and I hope you'll do the same in the comments below. Ready to go?&lt;/p&gt;

&lt;h2&gt;
  
  
  Native DOM Events &lt;a id="native-elements"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Native HTML elements communicate up the DOM tree using DOM events. You might be used to seeing this with elements like &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; which publish events like &lt;code&gt;change&lt;/code&gt; and &lt;code&gt;input&lt;/code&gt; or with the &lt;code&gt;&amp;lt;button /&amp;gt;&lt;/code&gt; element, where it's common to rely on the &lt;code&gt;click&lt;/code&gt; event that it publishes. It might not be immediately clear you are relying on these things, but when applying &lt;code&gt;onclick&lt;/code&gt; (native) or &lt;code&gt;onChange&lt;/code&gt; (virtual DOM) properties, it is these DOM events on which you are relying under the hood. Knowing that these events are dispatch along the DOM tree, we can choose locations (either explicit or general) at which to listen for them via the &lt;code&gt;addEventListener(type, listener[, options/useCapture])&lt;/code&gt; method that is present on any &lt;code&gt;HTMLElement&lt;/code&gt; based DOM node.&lt;/p&gt;

&lt;p&gt;These events have two phases; the "capture" phase and the "bubble" phase. During the capture phase, the event travels from the top of the DOM down towards the dispatching element and can be listened for on each of the elements that it passes through in this phase by setting the third argument of &lt;code&gt;addEventListener()&lt;/code&gt; to true, or by explicitly including &lt;code&gt;capture: true&lt;/code&gt; in an &lt;code&gt;options&lt;/code&gt; object passed as the third argument. For example the steps of the "capture" phase of a &lt;code&gt;click&lt;/code&gt; event on the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; in the following DOM structure:&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;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Click me!&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Would be as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, being a &lt;code&gt;click&lt;/code&gt; event, &lt;code&gt;bubbles: true&lt;/code&gt; is set by default, so the event would enter the "bubble" phase and travel back up the DOM passing through the above DOM in the following order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At any point in either phase that you are listening for this event, you will have access to the &lt;code&gt;preventDefault()&lt;/code&gt;, &lt;code&gt;stopPropagation()&lt;/code&gt;, and &lt;code&gt;stopImmediatePropagation()&lt;/code&gt; methods that give you powerful control over the events that travel across your application. &lt;code&gt;preventDefault()&lt;/code&gt; can most clearly be felt when listening to a &lt;code&gt;click&lt;/code&gt; event on an &lt;code&gt;&amp;lt;a href="..."&amp;gt;&lt;/code&gt; tag. In this context, it will &lt;em&gt;prevent&lt;/em&gt; the anchor link from being activated and prevent the page from navigating. In a way, this is the event asking for permission to do an action, and we'll look at this more closely in conjunction with manually dispatched events. &lt;code&gt;stopPropagation()&lt;/code&gt; prevents the event in question from continuing along the DOM tree and triggering subsequent listeners along that path, a sort of escape valve for the event when certain parameters are met. This can be taken one step further via &lt;code&gt;stopImmediatePropagation()&lt;/code&gt; which also prevents the event from completing the current step of the phase it is in. This means that no later bound listeners on that same DOM element for the event in question will be called. Returning to the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element in the example above, when a &lt;code&gt;click&lt;/code&gt; event is dispatched, you could imagine the following completely trivial listeners:&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;body&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;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;body&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;header&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;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;header&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;button&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;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;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// You can hear the `click` event during the "capture" phase on the `&amp;lt;body&amp;gt;` element.&lt;/span&gt;
&lt;span class="nx"&gt;body&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heard on `body` during "capture"&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// You cannot hear the `click` event during the "bubble" phase on the `&amp;lt;body&amp;gt;` element.&lt;/span&gt;
&lt;span class="nx"&gt;body&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not heard `body` during "bubble"&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;// You can hear the `click` event during the "bubble" phase on the `&amp;lt;header&amp;gt;` element.&lt;/span&gt;
&lt;span class="nx"&gt;header&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;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;e&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heard on `header` via listener 1 during "bubble"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPropagation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// You can hear the `click` event during the "bubble" phase on the `&amp;lt;header&amp;gt;` element.&lt;/span&gt;
&lt;span class="nx"&gt;header&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;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;e&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heard on `header` via listener 2 during "bubble"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopImmediatePropagation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// You cannot hear to the `click` event during the "bubble" phase on the `&amp;lt;header&amp;gt;`&lt;/span&gt;
&lt;span class="c1"&gt;// element being it is bound later than the previous listener and its use of the&lt;/span&gt;
&lt;span class="c1"&gt;// `stopImmediatePropagation()` method.&lt;/span&gt;
&lt;span class="nx"&gt;header&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;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;e&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not heard on `header` via listener 3 during "bubble"&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;// You can hear the `click` event during the "capture" phase on the `&amp;lt;button&amp;gt;` element.&lt;/span&gt;
&lt;span class="nx"&gt;button&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;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="nx"&gt;coonsole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heard on `button` during "capture"&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// heard on `body` during "capture"&lt;/span&gt;
&lt;span class="c1"&gt;// heard on `button` during "capture"&lt;/span&gt;
&lt;span class="c1"&gt;// heard on `header` via listener 1 during "bubble"&lt;/span&gt;
&lt;span class="c1"&gt;// heard on `header` via listener 2 during "bubble"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The majority of values for &lt;code&gt;bubbles&lt;/code&gt;, &lt;code&gt;cancelable&lt;/code&gt; (needed to empower &lt;code&gt;preventDefault()&lt;/code&gt;), and &lt;code&gt;composed&lt;/code&gt; are the same across native DOM events, and in many of those cases the value of &lt;code&gt;composed&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, so it's possible that the browser is already refuting the idea that it could "harmful". However, when working with native DOM events the values for these three properties are also not configurable. To access the power, and responsibility, that comes with being able to do so, you'll need to enter the world of manually dispatched events.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;dispatchEvent()&lt;/code&gt; &lt;a id="manual-dispatch"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So far we've mainly talked about the &lt;code&gt;click&lt;/code&gt; event as automatically dispatched by the browser. There is, of course, a whole family of UA-dispatched UI events that can be addressed in the same manner (e.g. &lt;code&gt;animationend&lt;/code&gt;/&lt;code&gt;copy&lt;/code&gt;/&lt;code&gt;keydown&lt;/code&gt;/&lt;code&gt;mouseover&lt;/code&gt;/&lt;code&gt;paste&lt;/code&gt;/&lt;code&gt;touch&lt;/code&gt;, etc.). However, the real fun starts when you take that power into your own hands and start dispatching events on your own creation. For this, the browser supplies us with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent" rel="noopener noreferrer"&gt;&lt;code&gt;dispatchEvent()&lt;/code&gt;&lt;/a&gt; method that hangs off of anything extended from &lt;code&gt;EventTarget&lt;/code&gt;, which includes all of the &lt;code&gt;HTMLElement&lt;/code&gt; based collection of DOM elements. For this to do its magic we need to supply it an event to dispatch. We're given a number of events classes to create our new event from (e.g. &lt;code&gt;new Event()&lt;/code&gt;, &lt;code&gt;new MouseEvent()&lt;/code&gt;, &lt;code&gt;new InputEvent()&lt;/code&gt;, etc.), but event just &lt;code&gt;new Event(typeArg[, initDict])&lt;/code&gt; gives us very a wide range of possibilities. &lt;/p&gt;

&lt;p&gt;Now, we're ready to dispatch an event.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;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;test-event&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;Event dispatched!&lt;/p&gt;

&lt;p&gt;The event has a &lt;code&gt;type&lt;/code&gt; of &lt;code&gt;test-event&lt;/code&gt;, so a listener set directly on the dispatching element will be able to hear it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;el&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;test-event&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;e&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// test-event&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can also listen for this event during the "capture" phase:&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;body&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;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;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;body&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;test-event&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;e&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&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="c1"&gt;// test-event&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But, you won't be hearing it in the "bubble" phase:&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;body&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;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;body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;body&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;test-event&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;e&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// ... ... Bueller?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is because by default a &lt;code&gt;new Event()&lt;/code&gt; (as well as all derivative event constructors) have &lt;code&gt;bubbles&lt;/code&gt;, &lt;code&gt;cancelable&lt;/code&gt;, and &lt;code&gt;composed&lt;/code&gt; set to &lt;code&gt;false&lt;/code&gt; by default. This is where the optional &lt;code&gt;initDict&lt;/code&gt; argument of our event constructor comes into play. When you want to customize the values of these, you'll create your event like so:&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;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;test-event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or however best supports (or least harms? 😉) the use case in question. That means that if you only want your event to be available in the "capture" phase (which literally means it takes half the time for it to run synchronously through your application than if it were to also make a pass through the "bubble" phase) you can leave that out. Don't have an action that you'd like permission to do? You can leave out &lt;code&gt;cancelable&lt;/code&gt;, too. Don't have shadow DOM? Decided definitively that &lt;code&gt;composed: true&lt;/code&gt; is harmful? It's your rodeo, leave it out!&lt;/p&gt;
&lt;h3&gt;
  
  
  Preventing Default &lt;a id="preventing-default"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Being able to prevent default on a manually dispatched event is awesome. It allows you to structure the actions you dispatch across your application as permission gates. Your event is essentially asking "do I have permission to do this thing?", and whether the answer to that question can be found nearby or far you'll be able to respond to that information as you see fit. Returning to our completely trivial sample DOM:&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;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Click me!&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Our button might want to dispatch a &lt;code&gt;hover&lt;/code&gt; event with &lt;code&gt;cancelable: true&lt;/code&gt; to ensure that in the current viewing context (as managed in a more central location) is an acceptable one for displaying &lt;code&gt;hover&lt;/code&gt; content or making hover related visuals, like maybe certain mobile browsers aught to do so we don't have to tap twice to get the actual link action to work... In this case, the application manager attached to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element will not grant permission to continue with this action:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&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;hover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;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;hover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;applyDefault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;applyDefault&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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;defaultPrevented&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Not only do we see this pattern in the native anchor tag, but you'll likely have noticed it in the various keyboard events, amongst many others. With &lt;code&gt;cancelable: true&lt;/code&gt; you can choose how closely to follow the patterns and practices applied natively by the browser.&lt;/p&gt;
&lt;h2&gt;
  
  
  The &lt;code&gt;detail&lt;/code&gt;s on Custom Events &lt;a id="details"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The ability for an event to outline that something &lt;em&gt;did&lt;/em&gt; (or is &lt;em&gt;about to&lt;/em&gt;) happen is a superpower in and of itself. However, there are cases when we want to know more than can be communicated via access to &lt;code&gt;e.target&lt;/code&gt; (a reference to the dispatching element), we want to know it more clearly, or we want the dispatching element to receive access to information only available to the listening element. For this, the off-the-shelf event constructors for native UI events won't be enough. Luckily, we have two really great options to work with when this is the case: &lt;code&gt;new CustomEvent()&lt;/code&gt; and &lt;code&gt;class MyEvent extends Event {}&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  CustomEvent &lt;a id="custom-event"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;new CustomEvent(typeArg[, initDict])&lt;/code&gt; can be used in your application exactly like any of the previous constructors we've discussed and is sometimes discussed as "the" interface by which to create manually dispatched events for its clever naming as a "custom" event. However, the real power that this constructor gives you is the inclusion of the &lt;code&gt;detail&lt;/code&gt; property on the &lt;code&gt;initDict&lt;/code&gt;. While &lt;code&gt;detail&lt;/code&gt; isn't directly writable after you have created the event, it can be set to an object or an array that won't lose identity when being mutated by the listener. This means that not only can you append data to it when dispatching an event, you can also append/edit data in it at the listener, allowing you to use events to resolve the value of data managed higher in your application. Get ready for another trivial example by imagining the following HTML:&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;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt; ... &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Resolving title...&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Resolving title...&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;From here text for our &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; could be resolved a la:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;body&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;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;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;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to find a title.&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;h1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;h1&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;event&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="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This all comes to pass thanks to the availability of the &lt;code&gt;detail&lt;/code&gt; property on the &lt;code&gt;initDict&lt;/code&gt; for &lt;code&gt;new CustomEvent()&lt;/code&gt; and the reality that DOM events are synchronous (meaning that by the time the line directly after &lt;code&gt;dispatchEvent()&lt;/code&gt; is run, the event will have already traveled every DOM node that its settings and listeners will allow), which can be super powerful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Extending Event &lt;a id="extending-event"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A very similar, and much more in-depth, form of customization can be had from extending the &lt;code&gt;Event&lt;/code&gt; base class. Immediately, this approach allows you to access data that you would hang off of the event without the intervening &lt;code&gt;detail&lt;/code&gt;. On top of that, the ability to use &lt;code&gt;instanceof&lt;/code&gt; is where this approach really differentiates itself. Returning to the HTML in the example above, let's now resolve the values for both of the headline elements:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;H1Title&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to find a title.&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;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&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;class&lt;/span&gt; &lt;span class="nc"&gt;H2Title&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to find a title.&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;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&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;body&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;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;H1Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;H2Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt; &lt;span class="nx"&gt;going&lt;/span&gt; &lt;span class="nx"&gt;places&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;;
    }
});

const h1Title = new H1Title();
const h2Title = new H2Title();

h1.dispatchEvent(h1Title);
h1.innerText = h1Title.title;

h2.dispatchEvent(h2Title);
h2.innerText = h2Title.title;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Whichever approach you take, using DOM events to pass actual data around your application can be very powerful. It's not a huge step from the trivial example above to a more complete Context API or a DOM bound Redux implementation. Versions of this approach can also serve as an orchestrator for asynchronous actions across your application. For more information on leveraging events in this way, check out this very informative talk by &lt;a href="https://twitter.com/justinfagnani" rel="noopener noreferrer"&gt;Justin Fagnani&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/x9YDQUJx2uw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
Side note: all of the events in the above video apply `composed: true`...


&lt;h2&gt;
  
  
  Events from the Shadow Root &lt;a id="shadow-root"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Up to this point, every event that we have discussed has been dispatched in a document without any shadow roots. Because of this, there have been no extenuating encapsulations to take into consideration meaning unless you were to leverage &lt;code&gt;stopPropagation()&lt;/code&gt; or &lt;code&gt;stopImmediatePropagation()&lt;/code&gt; on one of those events the "capture" phase would span the entire DOM tree from &lt;code&gt;document&lt;/code&gt; to the dispatching element, and when &lt;code&gt;bubbles: true&lt;/code&gt; the "bubble" phase would do the same in reverse. When attached to an element, a shadow root creates a sub-tree of DOM that is encapsulated from the main documents DOM tree. As discussed before, the majority of UA-dispatched UI events have &lt;code&gt;composed: true&lt;/code&gt; by default and will pass between the sub-tree to the main tree at will. Now that we know how to manually dispatch events, we get to choose whether that is true about the events we create.&lt;/p&gt;
&lt;h3&gt;
  
  
  Event Retargeting &lt;a id="event-retargeting"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Before we do that, let's take a look at what happens when an event with &lt;code&gt;composed: true&lt;/code&gt; is dispatched within a shadow root, being it will happen a lot (UA-dispatched UI events and all). Take, for example, a &lt;code&gt;click&lt;/code&gt; event (which also has &lt;code&gt;bubbles: true&lt;/code&gt; by default) as triggered by the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; in the following DOM tree:&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;document&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&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;shadow-root-el&amp;gt;&lt;/span&gt;
                #shadow-root
                    &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;
                            Click here!
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&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;/shadow-root-el&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As with an event in the light DOM, the &lt;code&gt;click&lt;/code&gt; event here will begin its "capture" phase at the &lt;code&gt;&amp;lt;document&amp;gt;&lt;/code&gt;. However, it's here that the first difference between light DOM and shadow DOM events will become clear, the &lt;code&gt;target&lt;/code&gt; of this event will not be the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element. As the shadow root on &lt;code&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/code&gt; is designed to do, it will have encapsulated the DOM inside of its sub-tree and hidden it away from the implementing document. In doing so, it will have retargeted the event in question to the &lt;code&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/code&gt; instead.&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;document&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "capture", target: `shadow-root-el` --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&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;shadow-root-el&amp;gt;&lt;/span&gt;
                #shadow-root
                    &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;
                            Click here!
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&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;/shadow-root-el&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The event will capture down the DOM tree with these settings until it enters the shadow root where we'll experience the next difference between light DOM and shadow DOM events. The shadow root is the first node in our sub-tree that encapsulates the internals of &lt;code&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/code&gt; meaning we are &lt;em&gt;inside&lt;/em&gt; of the encapsulated DOM and the internals are no longer obfuscated from us. Here the &lt;code&gt;target&lt;/code&gt; will be the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element on which the &lt;code&gt;click&lt;/code&gt; event explicitly occurred.&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;document&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&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;shadow-root-el&amp;gt;&lt;/span&gt;
                #shadow-root &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "capture", target: `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;button&amp;gt;&lt;/span&gt;
                            Click here!
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&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;/shadow-root-el&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;From here, the event, still being in its "capture" phase, will continue to travel down the DOM until it reaches its &lt;code&gt;target&lt;/code&gt; the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;. Here it will be available in the "capture" phase. It will also be available as the first step of the "bubble" phase before traveling back up the DOM.&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;document&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&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;shadow-root-el&amp;gt;&lt;/span&gt;
                #shadow-root
                    &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;
                            &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "capture", target: `button` --&amp;gt;&lt;/span&gt;
                            &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "bubble", target: `button` --&amp;gt;&lt;/span&gt;
                            Click here!
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&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;/shadow-root-el&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;During the "bubble" phase the same effect of encapsulation that the event experienced in the "capture" phase will be in play. While the target as the event passes the shadow root will be the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element, starting at the &lt;code&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/code&gt;, the event will be retargeted to that element before continuing to bubble up the DOM.&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;document&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&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;shadow-root-el&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "bubble", target: `shadow-root-el` --&amp;gt;&lt;/span&gt;
                #shadow-root &lt;span class="c"&gt;&amp;lt;!-- event: `click`, phase: "bubble", target: `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;button&amp;gt;&lt;/span&gt;
                            Click here!
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&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;/shadow-root-el&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;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Extended Retargeting &lt;a id="extended-retargeting"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;When working with nested shadow roots (e.g. custom elements with custom elements inside of them) this event retargeting will happen at each shadow boundary that the event encounters. That means that if there are three shadow roots that the event passed through the &lt;code&gt;target&lt;/code&gt; will change three times:&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;body&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;parent-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;parent-el&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;parent-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        #shadow-root &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;child-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;child-el&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;child-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                #shadow-root &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;grandchild-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;grandchild-el&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;grandchild-el&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        #shadow-root &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt; &lt;span class="na"&gt;--&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;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;target:&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                Click here!
                            &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- click happens here --&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;grandchild-el&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;child-el&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;parent-el&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is, of course, one of the benefits of the encapsulation that a shadow root can provide, what happens in the shadow root stays in the shadow root, or at least appears that way.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Composed Path Less Traveled &lt;a id="composed-path"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There are times when we need a look into that dirty laundry to get a peek at just where that event came from, be it &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, or something else (it's hopefully a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;...a11y, people!), and for those times we've got the &lt;code&gt;composedPath()&lt;/code&gt; method on our events. At any point in the event's lifecycle, calling &lt;code&gt;composedPath()&lt;/code&gt; on that event will give you an array of all the DOM elements on which it can be heard. The array is listed in "bubble" order (even when &lt;code&gt;bubbles: false&lt;/code&gt;), so the zeroeth item will be the dispatching element and the last item will be the last element through which the event will pass. That means you can always use the following code to ascertain the original dispatching element and outline the path along which the event will trave, assuming the previous example HTML:&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;composedPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;composedPath&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;originalDispatchingElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;composedPath&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;composedPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
    &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;grandchild&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's here in &lt;code&gt;composedPath()&lt;/code&gt; that the effects of &lt;code&gt;composed: true&lt;/code&gt; are most clearly felt. When an event has &lt;code&gt;composed: true&lt;/code&gt; that path will start from the original dispatching element all the way to the &lt;code&gt;window&lt;/code&gt; that holds the entire &lt;code&gt;document&lt;/code&gt;, but when an event has &lt;code&gt;composed: false&lt;/code&gt; that path will end at the shadow root that contains the dispatching element. &lt;/p&gt;
&lt;h2&gt;
  
  
  Decomposing an Event &lt;a id="composed"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As we've seen so far, what &lt;code&gt;composed: true&lt;/code&gt; does for an event is make it act as much like a native DOM event as possible by allowing its "capture" phase to start at the very root of the document (as well as across intervening shadow boundaries) and travel into the shadow DOM sub-tree where the original dispatching element lives before allowing the "bubble" phase to do the same in reverse. Along that path, the event will be further affected by the shadow roots that it passes through by having itself retargeted to the element on which that shadow root is attached. There is one more place where a &lt;code&gt;composed: true&lt;/code&gt; event in a shadow root will perform differently than when not in one. &lt;code&gt;composed: true&lt;/code&gt; allowing that event to cross the shadow root, it will fire (as if in the "bubble" phase, but without traveling up the DOM) on the element to which the shadow root is attached. That means (referencing the DOM below) that while a &lt;code&gt;composed: true, bubbles: false&lt;/code&gt; event that was dispatched on &lt;code&gt;&amp;lt;event-dispatching-element&amp;gt;&lt;/code&gt; would pass through all of the elements in the following code during the "capture", only the &lt;code&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/code&gt; would experience that event during the "bubble" phase.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;shadow-root-el&amp;gt;&lt;/span&gt;
        #shadow-root
            &lt;span class="nt"&gt;&amp;lt;section&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;event-dispatching-element&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So, it's really &lt;code&gt;composed: false&lt;/code&gt; that gives us new and interesting functionality.&lt;/p&gt;

&lt;p&gt;When an event is dispatched with &lt;code&gt;composed: false&lt;/code&gt; then that event will be contained within the shadow root in which it is fired. Right off, for the speed-obsessed developers reading this, that means your events will go faster! Whereas &lt;code&gt;{bubbles: false}&lt;/code&gt; can double the speed of an event by completely cutting off the "bubble" phase (read half of the traveling required of an event), &lt;code&gt;{composed: false}&lt;/code&gt; could cut that distance all the way down to two stops, the dispatching element and the shadow root that contains it, assuming such a simplified DOM tree. Code speed is likely not the concern here, even if it is worth noting. What's really of most interest is access. When an event is dispatched with &lt;code&gt;composed: false&lt;/code&gt; only the ancestor elements encapsulated in the same shadow root have access to it.&lt;/p&gt;

&lt;p&gt;Yes, not only does shadow DOM allow you to encapsulate your CSS, DOM, and javascript, it will contain your events for you as well essentially making the element a closed application ecosystem. Within your sub-tree you could dispatch any number of events, with as simple (as affords the contained scope) or complex (as affords their lack of being public) event names as you'd like, process them as needed internally, and then only when needed (or ready) dispatch a new, clearly documented, and explicitly packaged event into the parent scope. That parent scope could also be a shadow tree, and it can then do the same with the various events dispatched there. Turtle this approach all the way up and it becomes very clear how shadow DOM really empowers the reuse of components through this encapsulation. &lt;code&gt;composed: false&lt;/code&gt; is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields#Private_fields" rel="noopener noreferrer"&gt;private fields&lt;/a&gt; of DOM events.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Responsibility Part &lt;a id="responsibility"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So, what are we to make of all this power? And, what sort of trouble can it get us in? After all, the premise behind such a broad assertion as "&lt;code&gt;composed: true&lt;/code&gt; is harmful" is that it &lt;em&gt;will&lt;/em&gt;, after a turn, get us in trouble.&lt;/p&gt;

&lt;p&gt;My path towards examining this danger started with a conversation around the minutia that marks the difference between handing events via a passed callback and doing so via a listener. With a passed callback, you know that there is work that you need to do:&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;doWork&lt;/span&gt; &lt;span class="o"&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Do work.&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;And you pass it into the element that needs to do that work.&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;primaryButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;onClick&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;html&lt;/span&gt;&lt;span class="s2"&gt;`
    &amp;lt;button @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Primary Button&amp;lt;/button&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;primaryButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doWork&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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this way you can pass this callback from a great distance if you need:&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;doWork&lt;/span&gt; &lt;span class="o"&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Do work.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrimaryButton&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&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="nf"&gt;properties&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="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attribute&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="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;button @click=&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;onClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;Primary Button&amp;lt;/button&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-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;PrimaryButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&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="nf"&gt;properties&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="na"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attribute&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="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;div class="card"&amp;gt;
                &amp;lt;h1&amp;gt;Something&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;Some stuff...&amp;lt;/p&amp;gt;
                &amp;lt;primary-button .onClick=&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;doWork&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/primary-button&amp;gt;
            &amp;lt;/div&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Section&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&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="nf"&gt;properties&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="na"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attribute&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="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;section&amp;gt;
                &amp;lt;custom-card .doWork=&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;doWork&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/custom-card&amp;gt;
            &amp;lt;/section&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;custom-section .doWork=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/custom-section&amp;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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But, in the end, the work is done &lt;em&gt;AT&lt;/em&gt; the site of the event. In this way, even if you know work might need to be done high up in your application, you use a templating system (in the above example &lt;code&gt;lit-html&lt;/code&gt; via &lt;code&gt;LitElement&lt;/code&gt;, but attainable via myriad virtual DOM systems as well) to pass that action down to the event site. This approach works perfectly with &lt;code&gt;composed: false&lt;/code&gt; because with the callback passed into the &lt;code&gt;&amp;lt;primary-button&amp;gt;&lt;/code&gt; element only the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element therein really needs to know about the event that is being dispatched. However, we've just learned the &lt;code&gt;click&lt;/code&gt; events (and most other default UI-events) are dispatched with &lt;code&gt;composed: true&lt;/code&gt;, so that means we &lt;em&gt;could&lt;/em&gt; also do the following:&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;doWork&lt;/span&gt; &lt;span class="o"&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Do work.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrimaryButton&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;button&amp;gt;Primary Button&amp;lt;/button&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;primary-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;PrimaryButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;div class="card"&amp;gt;
                &amp;lt;h1&amp;gt;Something&amp;lt;/h1&amp;gt;
                &amp;lt;p&amp;gt;Some stuff...&amp;lt;/p&amp;gt;
                &amp;lt;primary-button&amp;gt;&amp;lt;/primary-button&amp;gt;
            &amp;lt;/div&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Section&lt;/span&gt; &lt;span class="nx"&gt;extend&lt;/span&gt; &lt;span class="nx"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
            &amp;lt;section&amp;gt;
                &amp;lt;custom-card&amp;gt;&amp;lt;/custom-card&amp;gt;
            &amp;lt;/section&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="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-section&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;custom-section @click=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/custom-section&amp;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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the above example, we &lt;em&gt;listen&lt;/em&gt; for the event, which is possible because the &lt;code&gt;click&lt;/code&gt; event has &lt;code&gt;composed: true&lt;/code&gt; by default. In theory, both samples of code output the same user experience, but that isn't true. While the passed callback example will ONLY call &lt;code&gt;doWork&lt;/code&gt; when the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element in the &lt;code&gt;&amp;lt;primary-button&amp;gt;&lt;/code&gt; element is clicked, the listening example will do so AS WELL AS calling &lt;code&gt;doWork&lt;/code&gt; when any other part of the &lt;code&gt;&amp;lt;custom-section&amp;gt;&lt;/code&gt; element is clicked: the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, etc. Here is the source of "&lt;code&gt;composed: true&lt;/code&gt; considered harmful". While the &lt;code&gt;composed: true&lt;/code&gt; event allows you to listen more easily to the event in question, it also hears a lot more than you might be expecting when opting into the practice. Via the passed callback approach you could also go one step further with your callback, leverage the &lt;code&gt;stopPropagation()&lt;/code&gt; method we discussed and prevent DOM elements that would naturally be later in the event lifecycle from hearing the event:&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;doWork&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopPropagation&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Do work.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We're feeling safe now, aren't we!?&lt;/p&gt;
&lt;h3&gt;
  
  
  Non-standard Events &lt;a id="non-standard-events"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;click&lt;/code&gt; event, and generally all &lt;code&gt;MouseEvents&lt;/code&gt;, is pretty powerful in this way: they can happen everywhere. Without passing a callback, you would be forced to rely on &lt;a href="https://davidwalsh.name/event-delegate" rel="noopener noreferrer"&gt;event delegation&lt;/a&gt; to contain the effects of such broadly felt/originated events. While this may seem powerful (and is leveraged in a very popular synthetic event system), it inherently breaks the encapsulation provided by the shadow DOM boundaries outlined by our custom elements. That is to say, if you &lt;em&gt;have&lt;/em&gt; to know that &lt;code&gt;&amp;lt;custom-section&amp;gt;&lt;/code&gt; has a &lt;code&gt;&amp;lt;custom-card&amp;gt;&lt;/code&gt; child that subsequently has a &lt;code&gt;&amp;lt;primary-button&amp;gt;&lt;/code&gt; child that then has a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; child, in order to respond to a click then why have encapsulation, to begin with? So, &lt;code&gt;composed: true&lt;/code&gt; is harmful, after all? I want to hear your thoughts below, but let's also take the following into account. When we manually dispatch events, we get to decide what those events are called.&lt;/p&gt;

&lt;p&gt;Our non-standard events, whether they're made via &lt;code&gt;new Event('custom-name')&lt;/code&gt; or &lt;code&gt;new CustomEvent('custom-name')&lt;/code&gt; or &lt;code&gt;class CustomNamedEvent extends Event { constructor() { super('custom-name'); } }&lt;/code&gt;, are completely under our control. This means we no longer have to worry about the generic nature of the &lt;code&gt;click&lt;/code&gt; event and can use a custom naming system to dispatch more specific (e.g. &lt;code&gt;importing-thing-you-care-about&lt;/code&gt;) event names. By this approach, we get back a good amount of control over our response to an event:&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;custom-section @importing-thing-you-care-about=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&amp;lt;/custom-section&amp;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;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this context, we can be fairly certain that nothing but what we expect to dispatch the &lt;code&gt;importing-thing-you-care-about&lt;/code&gt; event will be doing so. By this approach, we can listen from a distance, and be sure that only the element that we expect to dispatch an event is doing so, without having to resort to techniques like event delegation. Maybe that means we've been confusing &lt;code&gt;composed: true&lt;/code&gt; for "event delegation" this whole time... Does it make the use of &lt;code&gt;composed: true&lt;/code&gt; in this case safe? This starts to come down to the specific needs of your application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recap &lt;a id="recap"&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DOM events are very powerful (even when only looking at the &lt;code&gt;bubbles&lt;/code&gt;, &lt;code&gt;cancelable&lt;/code&gt;, and &lt;code&gt;composed&lt;/code&gt; settings as we have today) and can be leveraged for any number of things in an application.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;bubbles&lt;/code&gt; controls whether the event enters the second half or "bubble" phase of its lifecycle&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cancelable&lt;/code&gt; allows for &lt;code&gt;preventDefault()&lt;/code&gt; to send an approval signal back to the dispatching element&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;composed&lt;/code&gt; decides how the event relates to shadow DOM boundaries&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you've worked with these events before (whether in shadow DOM or not) you're likely accustomed to the way that almost all of them include &lt;code&gt;composed: true&lt;/code&gt; by default.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;composed: true&lt;/code&gt; opens the event to being listened for at a distance, so the naming of that event becomes more important.&lt;/li&gt;
&lt;li&gt;When passing a callback into a component for an event, &lt;code&gt;composed: false&lt;/code&gt; can give fine-grained control over an application's ability to react to that event.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  &lt;code&gt;composed: true&lt;/code&gt; considered harmful?
&lt;/h1&gt;

&lt;p&gt;With all this new knowledge, what do you think, should &lt;code&gt;composed: true&lt;/code&gt; be considered harmful? Is the browser killing us with a thousand cuts by setting all UA-dispatched UI events to &lt;code&gt;composed: true&lt;/code&gt; by default? It may be that &lt;code&gt;composed: true&lt;/code&gt; is for "apps" and &lt;code&gt;composed: false&lt;/code&gt; is for "components"...but, where do we draw the line? While I've used both values of &lt;code&gt;composed&lt;/code&gt; in my own manually dispatched events, I'd say that I've fallen on the side of &lt;code&gt;composed: true&lt;/code&gt; &lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-9-10j3"&gt;more often&lt;/a&gt; than not, though namely through lack of introspection than through a presence of planning. After walking through all of the above, it's hard to say one is specifically better/more dangerous than the other. If you've taken the time to watch the very informative video above, you'll have seen a plethora of quality use cases &lt;em&gt;for&lt;/em&gt; &lt;code&gt;composed: true&lt;/code&gt; when building for the web. Maybe &lt;code&gt;composed: true&lt;/code&gt; isn't harmful after all? One thing I am sure of is, like most technical decisions, the value you set for &lt;code&gt;composed&lt;/code&gt; should be decided based on the specific needs of your application and/or the offending component in question. However, my experience is just that, my experience. I'd love to hear about yours! Please hop into the comments below and share whether you've been harmed by &lt;code&gt;composed: true&lt;/code&gt; and how.&lt;/p&gt;
&lt;h2&gt;
  
  
  Want to do more research?
&lt;/h2&gt;

&lt;p&gt;Still wrapping your brain around what all this looks like? I've put together an event playground where you can test the various settings and realities we've discussed so far: &lt;/p&gt;


&lt;div class="glitch-embed-wrap"&gt;
  &lt;iframe src="https://glitch.com/embed/#!/embed/super-area?previewSize=100&amp;amp;path=index.html" alt="super-area on glitch"&gt;&lt;/iframe&gt;
&lt;/div&gt;



&lt;p&gt;While the design therein could certainly be considered &lt;em&gt;harmful&lt;/em&gt;, hopefully, it'll give you a more clear understanding of the settings that can be applied to events and how that affects the way those events travel around the DOM. Take note that each DOM element that hears an event will say so, along with the phase during which it heard the event, what step in the path of the event it passed through that element and the &lt;code&gt;target&lt;/code&gt; element at that point next to the original dispatching element. I use manually dispatched events pretty liberally across my applications and shadow DOM-based components, and putting this little ditty together went a long way to cementing my knowledge of DOM events (and surprised me in a couple of spots, too), so hopefully, it helps you too. As you get deeper into your studies, if you remix the project to help outline your thoughts on &lt;code&gt;composed: true&lt;/code&gt;, please share them with us all in the comments below.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Epilogue: originally this post had a reading time of 3 minutes, I swear! I thought, &lt;em&gt;get in, get out, get people talking about important things&lt;/em&gt;. Cooler heads pointed out that DOM events are not a simple subject and the knowledge base of even seasoned users (myself included) can be bumpy, so I &lt;em&gt;expanded&lt;/em&gt;. That being so, there is a lot of information here, so if you think I've missed something somewhere, please let me know! I intend to smooth out the bump knowledge, not make it worse, and your help in that endeavor would be much appreciated. I'd also like to thanks the &lt;a href="https://dev.to/open-wc"&gt;Open Web Components&lt;/a&gt; team for being those cooler heads and putting some hard-fought editing into this article. &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Not Another To-Do App: Part 10</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Tue, 01 Oct 2019 20:28:07 +0000</pubDate>
      <link>https://dev.to/westbrook/not-another-to-do-app-part-10-mp6</link>
      <guid>https://dev.to/westbrook/not-another-to-do-app-part-10-mp6</guid>
      <description>&lt;h4&gt;
  
  
  Getting your hands dirty and feet wet with Open Web Component Recommendations...sort of.
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This a cross-post of a Feb 26, 2019 article from &lt;a href="https://medium.com/@westbrook/not-another-to-do-app-e4c699bd4777" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; that takes advantage of my recent decision to use Grammarly in my writing (so, small edits have been made here and there), thanks for looking again if you saw it there 🙇🏽‍♂️ and if this is your first time reading, welcome!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Welcome to “Not Another To-Do App”, an overly lengthy review of making one of the smallest applications every developer ends up writing at some point or another. If you’re here to read up on a specific technique to writing apps or have made your way from a previous installation, then likely you are in the right place and should read on! If not, it’s possible you want to &lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;start from the beginning&lt;/a&gt; so you too can know &lt;a href="https://github.com/Westbrook/to-do-app" rel="noopener noreferrer"&gt;all of our characters’ backstories...&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’ve made it this far, why quit now?&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Reusable and Scaleable Data Management
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvnr8pivyyd9hsr1voij.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvnr8pivyyd9hsr1voij.jpeg" alt="Reusable and Scaleable Data Management" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@francesco_ungaro" rel="noopener noreferrer"&gt;Francesco Ungaro
&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;You know what’s kind of popular right now? &lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;Functional programming.&lt;/a&gt;&lt;/em&gt; You know what’s a great way to deliver features build to take advantage of functional programming concepts? &lt;em&gt;&lt;a href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/" rel="noopener noreferrer"&gt;ES Modules.&lt;/a&gt;&lt;/em&gt; You know what’s a great way to manage logic trees without relying on &lt;code&gt;switch&lt;/code&gt; or &lt;code&gt;if/else&lt;/code&gt; statements? &lt;em&gt;&lt;a href="https://twitter.com/jamesmh_dev/status/1092517108599193601" rel="noopener noreferrer"&gt;The strategies pattern.&lt;/a&gt;&lt;/em&gt; What do you get when you mix them all together? &lt;em&gt;Reusable and Scaleable Data Management.&lt;/em&gt; Or, at least I certainly hope so (yes, that’s another good one to take down to the comments)...here’s how I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reuse
&lt;/h2&gt;

&lt;p&gt;On the reusable end of the spectrum, let’s start with the functional interface by which we interact with the lifecycle of our list of to-dos (creating it, adding to-dos to it, and completing to-dos on it). Access to this can be gained via &lt;code&gt;import { newToDos, addToDo, completeToDo } from './to-do-manager';&lt;/code&gt;. Here we get functional controls to initialize a new set of to-dos (i.e. return []), add a to-do (i.e. &lt;code&gt;return [...todos, newTodo]&lt;/code&gt;), and remove a todo (i.e. &lt;code&gt;return [...todos.filter(todo =&amp;gt; todo.id !== completedId)]&lt;/code&gt;. Across each of these, we establish and maintain the identity of our individual to-dos while generating a new identity for the resulting list of to-dos, which will trigger the render pipeline in our &lt;code&gt;LitElement&lt;/code&gt; based application. Further, their being exports allows them to be used and reused across our application and tests while being prepared for being made an external dependency if at some point this data became useful across multiple applications. If you want to use them in your To-Do application, &lt;a href="https://github.com/Westbrook/to-do-app" rel="noopener noreferrer"&gt;fork me on GitHub&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Scale
&lt;/h2&gt;

&lt;p&gt;On the scaleable side of the spectrum, we’ll be looking at the rest of the code in &lt;code&gt;[src/to-do-manger.js](https://github.com/Westbrook/to-do-app/blob/master/src/to-do-manager.js)&lt;/code&gt; which covers the ability to deliver work level customized suggestions for the user via the &lt;code&gt;workLevelMessage(todos)&lt;/code&gt; export. While this data is also structured for use across the application and possible externalization as well, what’s really nice about it is how it is structured to scale.&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;function&lt;/span&gt; &lt;span class="nf"&gt;workLevelMeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&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;workLevelCounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workLevelByTodoCount&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;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workLevelCounts&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;todoCount&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;todoCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;workLevelByTodoCount&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;workLevelCounts&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;workLevelMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;workLevelMessages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;workLevelMeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&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;Notice how rather than attempting to structure an extended &lt;code&gt;if/then&lt;/code&gt; tree or &lt;code&gt;switch&lt;/code&gt; statement to outline which the message to return from &lt;code&gt;[workLevelMessage](https://github.com/Westbrook/to-do-app/blob/master/src/to-do-manager.js#L52)&lt;/code&gt; the &lt;code&gt;[workLevelMeter](https://github.com/Westbrook/to-do-app/blob/master/src/to-do-manager.js#L44)&lt;/code&gt; method relies on a series of object and array methods (thanks to our previous commit linting experience) to resolve the appropriate message. This means that regardless of whether to the current five levels of workloads and their associated messages, to a single one, or to fifty levels, this same logic will apply. We don’t need to go about adjusting what could easily become an awkward logic tree when you choose to make additions to the messages delivered or alterations to the levels at which their delivered, we just add the associated information to the data structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workLevelMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Looks like you don&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;t have anything to do right now. Take a break!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Looks like you&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;re almost done. Keep it up!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Still a little more work to do. Don&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;t loose focus!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;It&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s a tough job, but somebody&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s got to do it.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This may look like a lot, but I know you can do it!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Maybe it&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s time to take a vacation? I won&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;t judge.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/* New message */&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;There&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s a new message in town!&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workLevelByTodoCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/* New work level */&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above additions will immediately be available to uses with 50 or more to-dos, no logic alterations needed. And, even better, with the additional export of &lt;code&gt;[workLevelMessages](workLevelMessages)&lt;/code&gt; and &lt;code&gt;[workLevelByTodoCount](https://github.com/Westbrook/to-do-app/blob/master/src/to-do-manager.js#L36)&lt;/code&gt; those additions are also immediately added to the testing process.&lt;/p&gt;


&lt;h1&gt;
  
  
  And, in the end...
&lt;/h1&gt;

&lt;p&gt;If you are sure I’m wrong, want to celebrate how right I am, or teach me ways I can be even crazier/insightful/progressive/right/et al. about anything you’ve read this far, I hope you know the refrain...the comments you take, and equal to the comments you make. Or at least I heard (something like) that somewhere.&lt;/p&gt;

&lt;p&gt;As is noted above, the code shared throughout our conversation to date has not always been what ended up as final in my application. In case you’ve not gotten a chance to see what did, feel free to read the whole codebase on GitHub.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Westbrook" rel="noopener noreferrer"&gt;
        Westbrook
      &lt;/a&gt; / &lt;a href="https://github.com/Westbrook/to-do-app" rel="noopener noreferrer"&gt;
        to-do-app
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An open-wc powered To Do application
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8d1829ee80cf0f50b2f59537c74cece8ecf3eb61d851176fdf914c877d5b6a59/68747470733a2f2f6f70656e2d77632e6f72672f6865726f2e706e67"&gt;&lt;img width="200" src="https://camo.githubusercontent.com/8d1829ee80cf0f50b2f59537c74cece8ecf3eb61d851176fdf914c877d5b6a59/68747470733a2f2f6f70656e2d77632e6f72672f6865726f2e706e67"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Open-wc Starter App&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/open-wc" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4e4d866459c20be9bdc9f513c9c77ac627121ff58382571803bc88a22cdcb0c4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6275696c74253230776974682d6f70656e2d2d77632d626c75652e737667" alt="Built with open-wc recommendations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quickstart&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;To get started:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/open-wc/open-wc-starter-app.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; open-wc-starter-app

npm install
npm start&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://open-wc-starter-app.netlify.com/" rel="nofollow noopener noreferrer"&gt;Live demo&lt;/a&gt; on &lt;a href="https://open-wc.org/publishing/" rel="nofollow noopener noreferrer"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://open-wc-starter-app.netlify.com/" rel="nofollow noopener noreferrer"&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ushWL7Fd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/Westbrook/to-do-app./open-wc-starter-app.png"&gt;
  &lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Scripts&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;start&lt;/code&gt; runs your app with auto reload for development, it only works on browsers which support modules for faster builds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start:es5&lt;/code&gt; runs your app for development, it only works on browsers that don't support modules (IE11)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; builds your app for production and outputs it in the /dist folder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;start:build&lt;/code&gt; runs your built app using a plain web server, to prove it works without magic 😃&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build:stats&lt;/code&gt; creates an analysis report of your app bundle to be consumed by Webpack Visualizer and Analyser&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt; runs your test suite&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lint&lt;/code&gt; runs the linter for your project&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Westbrook/to-do-app" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;You can also see the &lt;a href="https://gifted-lamport-70b774.netlify.com/" rel="noopener noreferrer"&gt;&lt;em&gt;current&lt;/em&gt; final application&lt;/a&gt; on Netlify. It’s tied to the GitHub repo above, so by current...I really mean it. It’s putting all of the ideas we’ve discussed in action live on the internet, and I hope the conversations we’ve shared about the development of it have proved useful to you in some way.&lt;/p&gt;

&lt;p&gt;To bring us back to where we started, here’s a little refresher on the concepts that I wanted to find their way into my little To-Do app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;component-based code architecture&lt;/li&gt;
&lt;li&gt;custom property based style API&lt;/li&gt;
&lt;li&gt;event-based state management&lt;/li&gt;
&lt;li&gt;style sharing&lt;/li&gt;
&lt;li&gt;unit testing&lt;/li&gt;
&lt;li&gt;web component-based UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over the course of some tightly related and others wildly tangential approaches, I hope you agree that I did a decent job of making a pass at each and every one of them. Some of these techniques are enabled by the team behind Open Web Components and their brand new Starter App. Some of them are rooted deeply in the tools recommended therein. And, others are flights of fancy that I’ve cooked up or heard over the course of years working with applications on the web.&lt;/p&gt;

&lt;p&gt;If I haven’t said it yet, or in some cases if you hadn’t heard it yet, I’m not planting a stake in the ground to say that any of them are bar none the best in their class, and even less so am I trying to say that any of them would apply to projects of any size or &lt;a href="https://jasonformat.com/application-holotypes/" rel="noopener noreferrer"&gt;holotype&lt;/a&gt;. What I do hope is that maybe like yet another &lt;a href="https://www.youtube.com/watch?v=Vg60lf92EkM" rel="noopener noreferrer"&gt;set of interesting voices I follow in the field&lt;/a&gt; the subjects covered so far spark, not joy, but an interesting conversation between you and your other self, you and your team, you and me, or possibly even any small part of the larger javascript community. The more we get together to talk about our techniques and fret out the edge cases and the exactitudes that make them up, the happier we’ll be. (Oh, look, maybe I did mean for it to spark joy...) So, take the discussion to the comments below, the @ messages on &lt;a href="https://twitter.com/WestbrookJ" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, your favorite Slack, or &lt;a href="https://join.slack.com/t/polymer/shared_invite/enQtNTAzNzg3NjU4ODM4LTkzZGVlOGIxMmNiMjMzZDM1YzYyMzdiYTk0YjQyOWZhZTMwN2RlNjM5ZDFmZjMxZWRjMWViMDA1MjNiYWFhZWM" rel="noopener noreferrer"&gt;this one&lt;/a&gt; were working with the web platform, web components, LitElement, and Open Web Components are all trending (at least most of the time), and I’ll see you there!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fin.&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  The Short Game
&lt;/h1&gt;

&lt;p&gt;As voted on by a plurality of people with opinions on such topics that are both forced to see my tweets in their Twitter feed and had a free minute this last week, a 9000+ word article is a no, no.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1098196160613896192-36" src="https://platform.twitter.com/embed/Tweet.html?id=1098196160613896192"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1098196160613896192-36');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1098196160613896192&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;So, it is with the deepest reverence to you my dear reader that I’ve broken the upcoming conversations into a measly ten sections. Congratulations, you’re nearing the end of the first! If you’ve enjoyed yourself so far, or are one of those people that give a new sitcom a couple of episodes to hit its stride, here’s a list of the others for you to put on your Netflix queue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;Not Another To-Do App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-3jem"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-2m9a"&gt;Test Early, Test Often&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-4-58cd"&gt;Measure Twice, Lint Once&lt;/a&gt; (I, for one, welcome our robot overlords. Maybe they’ll do the writing every time I have an idea to deliver a crazy long series of articles like this...)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-5-5d7o"&gt;Make it a Component&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-6-an"&gt;Make it a Reusable Part&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-7-3cm7"&gt;Does Your Component Really Need to Know That?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-8-3lic"&gt;Separate Things Early, Often, and Only as Needed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-9-10j3"&gt;Some Abstractions Aren’t (Just) For Your App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reusable and Scaleable Data Management/And, in the end... (you are here)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gifted-lamport-70b774.netlify.com/" rel="noopener noreferrer"&gt;See the app in action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Special thanks to the team at &lt;a href="https://open-wc.org/" rel="noopener noreferrer"&gt;Open Web Components&lt;/a&gt; for the great set of tools and recommendations that they’ve been putting together to support the ever-growing community of engineers and companies bringing high-quality web components into the industry. &lt;a href="https://github.com/open-wc/open-wc" rel="noopener noreferrer"&gt;Visit them on GitHub&lt;/a&gt; and create an issue, submit a PR, or fork a repo to get in on the action!&lt;/p&gt;

</description>
      <category>openwc</category>
      <category>javascript</category>
      <category>data</category>
      <category>litelement</category>
    </item>
    <item>
      <title>Not Another To-Do App: Part 9</title>
      <dc:creator>Westbrook Johnson</dc:creator>
      <pubDate>Wed, 18 Sep 2019 13:03:51 +0000</pubDate>
      <link>https://dev.to/westbrook/not-another-to-do-app-part-9-10j3</link>
      <guid>https://dev.to/westbrook/not-another-to-do-app-part-9-10j3</guid>
      <description>&lt;h4&gt;
  
  
  Getting your hands dirty and feet wet with Open Web Component Recommendations...sort of.
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This a cross-post of a Feb 26, 2019 article from &lt;a href="https://medium.com/@westbrook/not-another-to-do-app-b5dce4d8931d" rel="noopener noreferrer"&gt;Medium&lt;/a&gt; that takes advantage of my recent decision to use Grammarly in my writing (so, small edits have been made here and there), thanks for looking again if you saw it there 🙇🏽‍♂️ and if this is your first time reading, welcome!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Welcome to “Not Another To-Do App”, an overly lengthy review of making one of the smallest applications every developer ends up writing at some point or another. If you’re here to read up on a specific technique to writing apps or have made your way from a previous installation, then likely you are in the right place and should read on! If not, it’s possible you want to &lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;start from the beginning&lt;/a&gt; so you too can know &lt;a href="https://github.com/Westbrook/to-do-app" rel="noopener noreferrer"&gt;all of our characters’ backstories...&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’ve made it this far, why quit now?&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Some Abstractions Aren’t (Just) For Your App
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj4nrcarnej4njdoin4r.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj4nrcarnej4njdoin4r.jpeg" alt="Some Abstractions Aren’t (Just) For Your App" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
Photo by &lt;a href="https://unsplash.com/@kadh" rel="noopener noreferrer"&gt;Kira auf der Heide&lt;/a&gt; on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;



&lt;p&gt;Often times we find ourselves staring the benefits of a quality abstraction in the eyes. For instance, if you realize that you are baking certain functionality into every component that you make, it’s probably a good idea to create an intermediary base class, or a mixin, or module export to abstract that repetition, centralize that functionality, and generally enable you to &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; (don’t repeat yourself) up your code. Other times, the benefits of making a solid abstraction are less clear. If something is really only used once in your application, it might be hard to fully sell the benefits of an abstraction (even to yourself, especially after I spent a lot of time telling you to be &lt;em&gt;lazy&lt;/em&gt; and separate things only when you need to in previous installations of this glorious adventure we’ve embarked on together), and in these cases it can be useful to think not only about where your application might be making use of this functionality, but where your tests might, as well. (I’ll leave the argument as to whether your tests are part of your app or not to my self-conscious desire to judge every statement in this series. Or, the comments, I still love comments!)&lt;/p&gt;

&lt;p&gt;As a refresher, let’s take a look at some testing code that we’ve already spent some time with:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adds a to do in response to a `todo-new` event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New To Do&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;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;open-wc-app&amp;gt;&amp;lt;/open-wc-app&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-new&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="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;nextFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&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;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&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 isolation, not that crazy; create a fixture, do a test, create/dispatch a Custom Event, wait a bit, do some more tests. Now, to add context, let’s look at some code from &lt;a href="https://github.com/Westbrook/to-do-app/blob/master/src/to-do-write.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/to-do-write.js&lt;/code&gt;&lt;/a&gt;:&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="nf"&gt;newToDo&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-new&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In isolation, also not crazy: check to see if a to-do has been supplied, create/dispatch a Custom Event, clean up the to do. Even side by side you could easily rely on &lt;a href="https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)" rel="noopener noreferrer"&gt;the rule of three&lt;/a&gt; as an excuse for not thinking too hard about this. There’s, of course, more...context. Take a look at the listener in &lt;a href="https://github.com/Westbrook/to-do-app/blob/master/src/open-wc-app.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/open-wc-app.js&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-new&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToDo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&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="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;todo-complete&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&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;No, this is not a third instance of the code above, but an eagle eye might have spotted the magic string that now resides across all three pieces of code. Tangentially in the land of context, you may also have noticed this code in &lt;a href="https://github.com/Westbrook/to-do-app/blob/master/test/to-do.test.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/to-do.js&lt;/code&gt;&lt;/a&gt;:&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="nf"&gt;completeToDo&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="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-complete&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As well as this somewhat matching test in &lt;code&gt;test/to-do.test.js&lt;/code&gt;:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;removes a to do in response to a `todo-complete` event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completeToDo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New To Do&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;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;open-wc-app
            todos='["&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]'
        &amp;gt;&amp;lt;/open-wc-app&amp;gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-complete&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="nx"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;nextFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&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="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to-do&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far we’ve seen the emergence of some magic strings that &lt;em&gt;might&lt;/em&gt; have been acceptable in the isolation of the application code. However, when they are placed next to their associated tests, and the somewhat magic Custom Events that the strings are found in, our “abstraction needed” bells should be going off. You, your application, and its tests are a little family, and while it certainly took me longer to realize that than it should have, abstractions aren’t for any one part of the family alone! Take a look at how we can abstract some of this away via &lt;a href="https://github.com/Westbrook/to-do-app/blob/master/src/to-do-events.js" rel="noopener noreferrer"&gt;&lt;code&gt;src/to-do-events.js&lt;/code&gt;&lt;/a&gt;:&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;eventOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toDoEventNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;COMPLETE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-complete&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toDoEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;eventOptions&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;todo&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventCompleteToDo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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;return&lt;/span&gt; &lt;span class="nf"&gt;toDoEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toDoEventNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPLETE&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventNewToDo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&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;return&lt;/span&gt; &lt;span class="nf"&gt;toDoEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toDoEventNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEW&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 have those pesky magic strings enumerated via &lt;code&gt;toDoEventNames.COMPLETE&lt;/code&gt;, and &lt;code&gt;toDoEventNames.NEW&lt;/code&gt;, and our Custom Event creation is sharing most of the operable parts of the process while exposing a helper to for each event to leverage that code. That means a good amount of complexity can be removed from the samples above and we get code like:&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="nf"&gt;newToDo&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eventNewToDo&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;todo&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;todo&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And:&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="nf"&gt;completeToDo&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="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eventCompleteToDo&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;todoId&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;Bubbling up to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;toDoEventNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToDo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&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="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;toDoEventNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPLETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;completeToDo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&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;Along the way we’ve also reduced maintenance costs, in the case we need to refactor, and future development cost, in the case we add new Custom Events for extended data interactions in the future.&lt;/p&gt;

&lt;p&gt;Speaking of data and managing it...you might be interested in checking out the next and last entry in our long-running telenovela.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Short Game
&lt;/h1&gt;

&lt;p&gt;As voted on by a plurality of people with opinions on such topics that are both forced to see my tweets in their Twitter feed and had a free minute this last week, a 9000+ word article is a no, no.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1098196160613896192-525" src="https://platform.twitter.com/embed/Tweet.html?id=1098196160613896192"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1098196160613896192-525');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1098196160613896192&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;So, it is with the deepest reverence to you my dear reader that I’ve broken the upcoming conversations into a measly ten sections. Congratulations, you’re nearing the end of the first! If you’ve enjoyed yourself so far, or are one of those people that give a new sitcom a couple of episodes to hit its stride, here’s a list of the others for you to put on your Netflix queue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-2kj9"&gt;Not Another To-Do App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-3jem"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-2m9a"&gt;Test Early, Test Often&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-4-58cd"&gt;Measure Twice, Lint Once&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-5-5d7o"&gt;Make it a Component&lt;/a&gt; (The intro/outro relationship on these articles could use some componentization...just apply bad joke via the Light DOM.)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-6-an"&gt;Make it a Reusable Part&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-7-3cm7"&gt;Does Your Component Really Need to Know That?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/westbrook/not-another-to-do-app-part-8-3lic"&gt;Separate Things Early, Often, and Only as Needed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Some Abstractions Aren’t (Just) For Your App (you are here)&lt;/li&gt;
&lt;li&gt;Reusable and Scaleable Data Management/And, in the end... (Coming Soon to &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gifted-lamport-70b774.netlify.com/" rel="noopener noreferrer"&gt;See the app in action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Special thanks to the team at &lt;a href="https://open-wc.org/" rel="noopener noreferrer"&gt;Open Web Components&lt;/a&gt; for the great set of tools and recommendations that they’ve been putting together to support the ever-growing community of engineers and companies bringing high-quality web components into the industry. &lt;a href="https://github.com/open-wc/open-wc" rel="noopener noreferrer"&gt;Visit them on GitHub&lt;/a&gt; and create an issue, submit a PR, or fork a repo to get in on the action!&lt;/p&gt;

</description>
      <category>openwc</category>
      <category>lithtml</category>
      <category>webcomponents</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
