<?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>Forem: Burton Smith</title>
    <description>The latest articles on Forem by Burton Smith (@stuffbreaker).</description>
    <link>https://forem.com/stuffbreaker</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%2F87382%2F524f7820-0def-4bf8-bea4-2beaf247d75d.jpeg</url>
      <title>Forem: Burton Smith</title>
      <link>https://forem.com/stuffbreaker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/stuffbreaker"/>
    <language>en</language>
    <item>
      <title>How Declarative Custom Elements (DCE) Could Improve Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Tue, 24 Mar 2026 12:06:44 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/how-declarative-custom-elements-dce-could-improve-web-components-53eg</link>
      <guid>https://forem.com/stuffbreaker/how-declarative-custom-elements-dce-could-improve-web-components-53eg</guid>
      <description>&lt;p&gt;In a previous article, I wrote about &lt;a href="https://dev.to/stuffbreaker/web-components-and-ssr-2024-edition-1nel"&gt;the state of SSR for Web Components&lt;/a&gt;. Since then, I have received many questions about when we will see Declarative Custom Elements (DCEs). Teams are looking for ways to eliminate JavaScript requirements for presentational components, address FOUC, and simplify SSR solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Challenges with Web Components
&lt;/h2&gt;

&lt;p&gt;As the adoption of web components continues to grow, teams encounter challenges with their client-side-only nature. Components require JavaScript for initial rendering, which can lead to Flash of Unstyled Content (FOUC). Server-side rendering implementations require framework-specific integrations and are often complex. Even purely presentational components need JavaScript to bootstrap their markup and styles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requires JavaScript
&lt;/h3&gt;

&lt;p&gt;Even for purely presentational components that consist only of markup and CSS, JavaScript is required to bootstrap the component definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flash of Unstyled Content
&lt;/h3&gt;

&lt;p&gt;A frequently cited issue with web components is FOUC, which occurs during page load.&lt;/p&gt;

&lt;p&gt;This issue is particularly problematic for teams using content management systems that lazy-load components for performance optimization. The delay between initial page load and component rendering creates a suboptimal user experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For information on how to reduce FOUC on your pages, check out &lt;a href="https://dev.to/stuffbreaker/reducing-fouc-with-web-components-1jnh"&gt;my article&lt;/a&gt; on the topic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Server-Side Rendering (SSR)
&lt;/h3&gt;

&lt;p&gt;SSR support for web components requires framework-specific implementations, often with specific configuration requirements and restrictions on APIs that can be used in component authoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative Shadow DOM
&lt;/h2&gt;

&lt;p&gt;Recognizing these pain points, the web platform introduced Declarative Shadow DOM as a solution. While DSD made progress, it brought its own set of trade-offs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;DSD templates cannot be reused - they must be repeated for each component instance on a page. Got 50 buttons? That's 50 complete shadow DOM templates - styles and all. Here's a &lt;em&gt;very&lt;/em&gt; simple example of what that looks like:&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;my-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;
  My Button 1
&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;
  My Button 2
&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Repeat for EVERY custom button... --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the limitation here. Each component instance requires the complete template, resulting in significant duplication of styles and markup across the page. This is the HTML equivalent of repeating a function definition instead of calling a reusable function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Library Dependency
&lt;/h3&gt;

&lt;p&gt;In order to hydrate web components with a Declarative Shadow DOM, teams depend on library-specific integrations to parse and inject the component's content into the DOM. This often comes with particular configuration requirements and constraints on component authoring patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSR Complexity
&lt;/h3&gt;

&lt;p&gt;Server-side rendering of client-side code has no standard. Each framework implements its own approach. That means for your web components to be server-rendered in frameworks, you need a special implementation for each of those frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Declarative Custom Elements
&lt;/h2&gt;

&lt;p&gt;The core concept behind DCEs is simple: define a custom element template once and reuse it throughout the document. This addresses the duplication issues inherent in DSD while maintaining the benefits of declarative markup.&lt;/p&gt;

&lt;p&gt;Here's what a simple DCE definition could look like:&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;definition&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"my-element"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"closed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/definition&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; All DCE examples and APIs in the article are hypothetical and have not been defined yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first phase of this could basically be a reusable Declarative Shadow DOM. The definition is specified once, then the element can be used multiple times throughout the document. No duplication, no JavaScript required for initial rendering, and no FOUC.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Real Example
&lt;/h3&gt;

&lt;p&gt;Let's look at a more realistic example. Here's a simple badge component with multiple variants and styling.&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;definition&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"my-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--badge-fg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;hollow&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--badge-fg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"danger"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--badge-bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"warning"&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="py"&gt;--badge-fg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-bg-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="py"&gt;--badge-border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;yellow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nc"&gt;.base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-fg-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-bg-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--badge-border-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt; &lt;span class="na"&gt;part=&lt;/span&gt;&lt;span class="s"&gt;"base"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/definition&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can use it anywhere in your markup:&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;my-badge&amp;gt;&lt;/span&gt;Default&lt;span class="nt"&gt;&amp;lt;/my-badge&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-badge&lt;/span&gt; &lt;span class="na"&gt;hollow&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hollow&lt;span class="nt"&gt;&amp;lt;/my-badge&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-badge&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"danger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Danger&lt;span class="nt"&gt;&amp;lt;/my-badge&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-badge&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"warning"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Warning&lt;span class="nt"&gt;&amp;lt;/my-badge&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each badge renders immediately with the correct styles. The browser can render &lt;code&gt;&amp;lt;my-badge&amp;gt;&lt;/code&gt; elements because the definition is available declaratively in the document.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of DCEs
&lt;/h3&gt;

&lt;h4&gt;
  
  
  No JavaScript Required
&lt;/h4&gt;

&lt;p&gt;Removing the JavaScript requirement for CSS-only components provides significant benefits. Many design system components are purely presentational, like badges and layout components, which don't require interactivity. With DCEs, these components function correctly even when JavaScript is disabled, slow to load, or blocked.&lt;/p&gt;

&lt;h4&gt;
  
  
  Improved FOUC Experience
&lt;/h4&gt;

&lt;p&gt;DCEs address the Flash of Unstyled Content issue directly. Components render immediately with correct styles because the browser has access to the template definition from the start. This eliminates the delay while JavaScript loads, executes, and defines custom elements.&lt;/p&gt;

&lt;h4&gt;
  
  
  Simplified Server-Side Rendering
&lt;/h4&gt;

&lt;p&gt;DCEs could greatly simplify and standardize the SSR implementation for a more framework-agnostic approach to pre-rendering components. Instead of relying on framework-specific integrations with their own quirks and restrictions, you could pre-render DCEs in a standardized way that works anywhere.&lt;/p&gt;

&lt;h4&gt;
  
  
  Progressive Enhancement
&lt;/h4&gt;

&lt;p&gt;DCEs could support progressive enhancement naturally. If interactivity is needed, custom elements can be upgraded by defining them with JavaScript. The DCE provides the initial render, and JavaScript adds behavior when available - combining the benefits of both approaches. This hybrid approach means your design system components work immediately for all users, while JavaScript-enhanced features activate seamlessly for those with JS enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Use Them
&lt;/h3&gt;

&lt;p&gt;How can we translate our existing components into DCEs and make sure they end up on the page when we need them?&lt;/p&gt;

&lt;p&gt;I think there are many ways to solve this problem. Much of this will depend on your development environment, tooling, and framework preferences.&lt;/p&gt;

&lt;p&gt;I made a &lt;a href="https://www.npmjs.com/package/@wc-toolkit/dce-generator" rel="noopener noreferrer"&gt;simple proof-of-concept&lt;/a&gt; that parses the &lt;a href="https://dev.to/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0"&gt;Custom Elements Manifest&lt;/a&gt; and generates them in various formats, including framework wrappers, so they can be provided by libraries and easily imported into an application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Again, all DCE examples and APIs in this article are hypothetical and have not been defined yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Real-World Performance Analysis
&lt;/h2&gt;

&lt;p&gt;I conducted performance experiments on a site using &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt;, a popular web component library, and the DCE Generator mentioned above, to evaluate the practical impact of DCEs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test setup:&lt;/strong&gt; A page with 1,362 custom elements - 943 of them Shoelace components, using 22 different components from the library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DSD approach:&lt;/strong&gt; Generating Declarative Shadow DOM for each component instance added about &lt;code&gt;1.9MB&lt;/code&gt; of markup and CSS&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DCE approach:&lt;/strong&gt; Loading &lt;em&gt;all&lt;/em&gt; 58 Shoelace DCE definitions added about &lt;code&gt;150KB&lt;/code&gt; of markup and CSS&lt;/p&gt;

&lt;p&gt;This represents a significant difference: &lt;code&gt;150KB&lt;/code&gt; versus &lt;code&gt;1.9MB&lt;/code&gt; - approximately 12x smaller. This also does not include the contents of the other 419 components on the page. Additionally, the DCE payload size remains constant regardless of component instance count, while DSD increases with each component instance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This page has since been refactored to be more lightweight (thankfully), but pages like this are not outside the realm of possibility for large, complex applications.&lt;/p&gt;

&lt;p&gt;The good news is that because of the repetition of the templates, the DSD example was efficiently gzipped and was not much larger than the DCE version for file transfer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These findings suggest that DCEs offer predictable performance characteristics and scalable markup strategies, especially for large applications with many repeated components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Challenges and Open Questions
&lt;/h2&gt;

&lt;p&gt;While the proposal offers clear benefits, there are some exciting design questions to explore as the specification develops.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scope Management
&lt;/h3&gt;

&lt;p&gt;Discussions about DCEs tend to expand into related APIs and features. Maintaining focus on a minimal, achievable initial proposal while acknowledging future extensibility is an ongoing challenge.&lt;/p&gt;

&lt;p&gt;Future phases of this API should include things like attribute mapping and integration with DOM parts, but the initial proposal of reusable shadow DOMs would provide huge benefits in performance and user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Implementation
&lt;/h3&gt;

&lt;p&gt;Achieving consensus and implementation across all major browsers presents coordination challenges. However, DCEs build on existing web platform primitives like templates and custom elements, which may facilitate implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I use DCEs today?
&lt;/h3&gt;

&lt;p&gt;No. DCEs are a proposal, and all APIs shown in this article are hypothetical. No browser has implemented them yet, and the specification is still being defined. &lt;/p&gt;

&lt;p&gt;However, the proof-of-concept DCE Generator package on npm lets you experiment with the concept by parsing your Custom Elements Manifest and generating definitions in various formats, including framework wrappers.&lt;/p&gt;

&lt;h3&gt;
  
  
  How are DCEs different from Declarative Shadow DOM?
&lt;/h3&gt;

&lt;p&gt;DSD lets you define shadow DOM declaratively, but requires a full template copy for every single component instance on the page. DCEs build on that foundation by allowing you to define the template once and reuse it across all instances, solving DSD's core scalability and framework integration problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about attributes?
&lt;/h3&gt;

&lt;p&gt;Attributes are a key aspect of custom elements. I have &lt;a href="https://github.com/break-stuff/declarative-custom-elements/blob/main/attributes.md" rel="noopener noreferrer"&gt;an API proposal&lt;/a&gt; for this, but attribute binding would likely be a fast-follow feature since they would depend on a feature like &lt;a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/DOM-Parts.md" rel="noopener noreferrer"&gt;DOM Parts&lt;/a&gt;, whose spec is still in development.&lt;/p&gt;

&lt;p&gt;Because it depends on a feature that is still in development and not required to address some of these initial issues, it would be a shame not to hold these features hostage waiting for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about conditional rendering?
&lt;/h3&gt;

&lt;p&gt;Some components render different content depending on defined attributes or slotted content (ie - button components that can render a button or an anchor tag). This would add a great deal of flexibility to components, but, like attributes, would likely depend on specs that aren't available yet, like &lt;a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/DOM-Parts.md" rel="noopener noreferrer"&gt;DOM Parts&lt;/a&gt; and additional elements or attributes to make it work. This would need to be a feature that comes in a future iteration of the spec.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about lifecycle hooks?
&lt;/h3&gt;

&lt;p&gt;In their initial form, they probably won't have lifecycle hooks. The core proposal is focused on declarative, presentational rendering - essentially a reusable shadow DOM template that requires no JavaScript. Lifecycle hooks like &lt;code&gt;connectedCallback&lt;/code&gt;, &lt;code&gt;disconnectedCallback&lt;/code&gt;, and &lt;code&gt;attributeChangedCallback&lt;/code&gt; are fundamentally JavaScript APIs and wouldn't fit the no-JS nature of a pure DCE. &lt;/p&gt;

&lt;p&gt;If access to those is needed, they can be included in the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements" rel="noopener noreferrer"&gt;autonomous custom element&lt;/a&gt; defined in JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should these only work with Shadow DOM?
&lt;/h3&gt;

&lt;p&gt;Autonomous custom elements can be rendered without a shadow root, so should DCEs allow rendering into the light DOM, not just the shadow DOM? This could broaden their applicability and simplify integration with existing markup, but it also introduces questions about style encapsulation and component boundaries.  &lt;/p&gt;

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

&lt;p&gt;Declarative Custom Elements represent a natural evolution of the web platform's component story. By building on the foundation of Declarative Shadow DOM and addressing its scalability limitations, DCEs promise to make web components more accessible, performant, and developer-friendly.&lt;/p&gt;

&lt;p&gt;Declarative Shadow DOM has been a great step in improving the web component ecosystem. Declarative Custom Elements feel like the next evolution of that vision, with exciting capabilities for dynamic templating and data mapping with technologies like DOM Parts to follow.&lt;/p&gt;

</description>
      <category>html</category>
      <category>javavscript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Reducing FOUC with Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Sat, 31 Jan 2026 19:11:10 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/reducing-fouc-with-web-components-1jnh</link>
      <guid>https://forem.com/stuffbreaker/reducing-fouc-with-web-components-1jnh</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;One of the best things about web components is that they are &lt;em&gt;wicked&lt;/em&gt; fast, but one of the common complaints I hear is that, because they are a client-side technology, there can be a delay between when the page renders and the components are defined and rendered. This can result in a flash of unstyled content (FOUC) and page content shifts - that jarring moment when your carefully crafted layout suddenly jumps around before your users' eyes. Not exactly the first impression we're going for.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can see each of these solutions described in this article in action in &lt;a href="https://break-stuff.github.io/ce-fouc-demo" rel="noopener noreferrer"&gt;this demo site&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Old Solution
&lt;/h2&gt;

&lt;p&gt;Here's the solution that's been making the rounds for ages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:defined&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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;
  
  
  What's happening?
&lt;/h3&gt;

&lt;p&gt;This CSS selector targets any custom element that hasn't been defined yet and visually hides it from view. Once the JavaScript defines the custom element, the browser marks it as &lt;code&gt;:defined&lt;/code&gt;, the selector no longer matches, and the component becomes visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performant and simple&lt;/strong&gt;: Just drop it in a CSS reset or theme file, and you're done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero setup required&lt;/strong&gt;: Developers using your components don't need to do anything special - it just works out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Layout shifts&lt;/strong&gt;: Because this only hides the component visually (the element still takes up space in the layout). The element initially renders as an unstyled &lt;code&gt;HTMLUnknownElement&lt;/code&gt;, which has no intrinsic size. As components load and apply their styles, it can result in content jumps as the page reflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent failures&lt;/strong&gt;: If a component fails to define (network issues, JavaScript errors, etc.), it will never be shown. This might be acceptable in some cases, but it's problematic if you're using custom elements for progressive enhancement or mixing defined and undefined custom elements in your application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility issues&lt;/strong&gt;: Using &lt;code&gt;visibility: hidden;&lt;/code&gt; can remove the content from the accessibility tree, which means screen readers and other assistive technologies can't access it. Your content is invisible to both sighted users and accessibility tools, which can affect initial focus.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving it with JavaScript
&lt;/h2&gt;

&lt;p&gt;In a &lt;a href="https://dev.to/stuffbreaker/do-you-need-to-ssr-your-web-components-2oao#but-what-about-fouc"&gt;previous article I wrote&lt;/a&gt;, I show how to use native JavaScript APIs to improve upon the original solution:&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;style&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;/* Visually block initial render as components get defined */&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.wc-loaded&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="c1"&gt;// Select all undefined custom elements on the page and wait for them to be loaded.&lt;/span&gt;
    &lt;span class="c1"&gt;// Once they are all loaded, add the `wc-loaded` class to the body to make it visible again.&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;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:not(:defined)&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;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;component&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="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;component&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="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="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="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wc-loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Add fallback to add the `wc-loaded` class to the body to make it visible after 200ms. &lt;/span&gt;
    &lt;span class="c1"&gt;// This prevents the user from being blocked from using your application in case a component fails to load&lt;/span&gt;
    &lt;span class="c1"&gt;// or other undefined elements are being used on the page.&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wc-loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's happening?
&lt;/h3&gt;

&lt;p&gt;This approach waits for all custom elements to be defined before revealing the page. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hide the body&lt;/strong&gt;: We set &lt;code&gt;opacity: 0&lt;/code&gt; on the body until it gets the &lt;code&gt;wc-loaded&lt;/code&gt; class. Unlike &lt;code&gt;visibility: hidden&lt;/code&gt;, this keeps content in the accessibility tree - screen readers can still access it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query for undefined elements&lt;/strong&gt;: &lt;code&gt;document.querySelectorAll(":not(:defined)")&lt;/code&gt; finds all custom elements that haven't been defined yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wait for definition&lt;/strong&gt;: For each undefined element, we use &lt;code&gt;customElements.whenDefined()&lt;/code&gt;, which returns a Promise that resolves when that custom element gets defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wait for all components&lt;/strong&gt;: &lt;code&gt;Promise.allSettled()&lt;/code&gt; waits for all those Promises to complete (whether they succeed or fail), then adds the &lt;code&gt;wc-loaded&lt;/code&gt; class to make everything visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fallback timeout&lt;/strong&gt;: The &lt;code&gt;setTimeout&lt;/code&gt; ensures that even if something goes wrong, users aren't staring at a blank page forever. After 200ms, we show the page regardless.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility-friendly&lt;/strong&gt;: By using &lt;code&gt;opacity&lt;/code&gt; instead of &lt;code&gt;visibility: hidden&lt;/code&gt;, the page content remains available to assistive technologies. Users with screen readers aren't left in the dark.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation&lt;/strong&gt;: Provides a fallback in case components fail to load or if undefined custom elements are also being used on the page. Your users will see &lt;em&gt;something&lt;/em&gt; rather than nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Very performant&lt;/strong&gt;: Modern browsers handle opacity changes efficiently, and the JavaScript overhead is minimal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proof&lt;/strong&gt;: By adding a class instead of directly manipulating styles, you avoid issues where new components added after page load might cause the opacity to flash in and out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup required&lt;/strong&gt;: Depending on your architecture, this might need to be included in your base template or injected into every page. It's not quite as "set it and forget it" as a CSS-only solution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript dependency&lt;/strong&gt;: Requires JavaScript to execute on the page before content is visible. If JS is disabled or fails to load, users see nothing (though the timeout helps mitigate this).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module timing&lt;/strong&gt;: The script needs to run as a module, which means it's deferred and runs after the DOM is loaded. This can introduce a small delay compared to inline scripts or CSS-only solutions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Potential failure point&lt;/strong&gt;: If JavaScript is disabled or fails to load the script, it could prevent the page from loading properly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving it with CSS
&lt;/h2&gt;

&lt;p&gt;What if we could have the best of both worlds - a solution that's easy to set up, lightweight, doesn't require additional JavaScript to run, &lt;em&gt;and&lt;/em&gt; provides a fallback if components fail to load? Here's a CSS-only approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* must define property to work properly for all browsers */&lt;/span&gt;
&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--wc-loaded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"&amp;lt;number&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* start timeout regardless if there are undefined components */&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;showBody&lt;/span&gt; &lt;span class="m"&gt;0s&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="m"&gt;200ms&lt;/span&gt; &lt;span class="n"&gt;forwards&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* if there are undefined components, hide until defined or 200ms - whichever comes first */&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;:has(:not(:defined))&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--wc-loaded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* update variable to prevent opactity flash for subsequent component loads */&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;showBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--wc-loaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;
  
  
  What's happening?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;@property --wc-loaded&lt;/code&gt;&lt;/strong&gt; - Defines a custom CSS property with an initial value of &lt;code&gt;0&lt;/code&gt;. This is required for proper browser compatibility and serves as a flag to track component loading state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;animation: showBody 0s linear 200ms forwards&lt;/code&gt;&lt;/strong&gt; - Sets up a zero-duration animation with a 200ms delay. This serves two purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a timeout mechanism that ensures the page displays after 200ms even if components are slow to load&lt;/li&gt;
&lt;li&gt;Updates the &lt;code&gt;--wc-loaded&lt;/code&gt; property to &lt;code&gt;1&lt;/code&gt; after the delay&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;:not(:defined)&lt;/code&gt;&lt;/strong&gt; - A powerful CSS pseudo-class that selects any custom elements that haven't been registered with the browser yet. This includes all web components that are still loading or haven't been defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;body:has(:not(:defined))&lt;/code&gt;&lt;/strong&gt; - Uses the &lt;code&gt;:has()&lt;/code&gt; pseudo-class to check if the body contains ANY undefined custom elements. This selector dynamically evaluates as components are defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;opacity: var(--wc-loaded, 0)&lt;/code&gt;&lt;/strong&gt; - Sets the body opacity to the value of &lt;code&gt;--wc-loaded&lt;/code&gt; (defaulting to 0). When undefined components exist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initially: opacity is &lt;code&gt;0&lt;/code&gt; (page hidden)&lt;/li&gt;
&lt;li&gt;After 200ms: &lt;code&gt;--wc-loaded&lt;/code&gt; becomes &lt;code&gt;1&lt;/code&gt; (page shows)&lt;/li&gt;
&lt;li&gt;After all components load: the &lt;code&gt;:has(:not(:defined))&lt;/code&gt; selector stops matching, removing the opacity override entirely&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;@keyframes showBody&lt;/code&gt;&lt;/strong&gt; - Defines the animation that updates the &lt;code&gt;--wc-loaded&lt;/code&gt; property to &lt;code&gt;1&lt;/code&gt;. This prevents opacity flashing when new components are dynamically added to the page after initial load.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Magic:&lt;/strong&gt; Once all custom elements are registered, the &lt;code&gt;:not(:defined)&lt;/code&gt; selector no longer matches anything, causing the &lt;code&gt;body:has(:not(:defined))&lt;/code&gt; condition to become false. This removes the opacity rule, and the page becomes fully visible. For fast connections, the 200ms timeout ensures minimal delay, while slow connections still show content rather than leaving users with a blank screen indefinitely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure CSS solution&lt;/strong&gt;: No JavaScript required! This means it works even in environments where JS is disabled or fails to load, and it executes immediately without waiting for the DOM or module loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic fallback&lt;/strong&gt;: The animation delay provides a built-in timeout. If components fail to define, the page still becomes visible after 200ms. No need for manual &lt;code&gt;setTimeout&lt;/code&gt; calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt;: Just a few lines of CSS - no extra scripts, no DOM queries, no Promise wrangling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive&lt;/strong&gt;: The &lt;code&gt;:has()&lt;/code&gt; selector automatically updates when elements become defined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to customize&lt;/strong&gt;: Want a longer timeout? Change the animation delay. Want to target specific containers instead of the whole body? Adjust the selector. Timeouts and animation timing can be made configurable using CSS variables. It's flexible without being complicated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility-friendly&lt;/strong&gt;: Like the JS solution, this uses &lt;code&gt;opacity&lt;/code&gt; rather than &lt;code&gt;visibility&lt;/code&gt;, keeping content available to assistive technologies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser support&lt;/strong&gt;: The &lt;code&gt;:has()&lt;/code&gt; selector is relatively new. It's supported in all modern browsers, but may not be supported in older versions. The good news is that if something is not supported, it will gracefully fail rather than crash your app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Timeout Length
&lt;/h2&gt;

&lt;p&gt;Please keep in mind that this is blocking the visibility of the page until the components are defined or until the timeout has lapsed. The longer the delay is, the more it can negatively impact the user experience. &lt;/p&gt;

&lt;p&gt;Here are some observations about the length of time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anything greater than 500 milliseconds there is a perceptible delay in the page load&lt;/li&gt;
&lt;li&gt;Anything greater than 1 second begins to negatively affect the &lt;a href="https://web.dev/articles/vitals#core-web-vitals" rel="noopener noreferrer"&gt;Core Web Vital&lt;/a&gt; scores, and you run the risk of subsequent opacity flashes if you have components added after the initial page load and before the timeout lapses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recommend keeping the timeout less than 500 milliseconds for the smoothest experience. That will allow your components and page content to load in a uniform manner without perceptible delays for the user. &lt;/p&gt;

&lt;p&gt;If your components are taking longer than that to load, you may want to look into a combination of these solutions to provide a good user experience without negatively impacting the overall user experience for the rest of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;These solutions will continue to evolve, but FOUC doesn't have to be a necessary evil of using web components. With these techniques in your toolkit, you can provide smooth, accessible experiences for your users and boost those Core Web Vitals scores without sacrificing the benefits of web components.&lt;/p&gt;

</description>
      <category>css</category>
      <category>javascript</category>
      <category>webcomponents</category>
      <category>customelements</category>
    </item>
    <item>
      <title>Creating Truly Custom Events for Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Wed, 25 Jun 2025 12:54:38 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/creating-truly-custom-events-for-web-components-5bp5</link>
      <guid>https://forem.com/stuffbreaker/creating-truly-custom-events-for-web-components-5bp5</guid>
      <description>&lt;p&gt;When building web components, communication between components and their parent applications is crucial. While the built-in DOM events like &lt;code&gt;click&lt;/code&gt; and &lt;code&gt;change&lt;/code&gt; work well for standard interactions, custom events give you the power to create meaningful, semantic event types that are tailored for your components.&lt;/p&gt;

&lt;p&gt;In this post, we'll explore how to extend JavaScript's &lt;code&gt;Event&lt;/code&gt; class to create custom events that are both powerful and maintainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Extend the Event Class?
&lt;/h2&gt;

&lt;p&gt;Before diving into the how, let's understand the why we would want to extend the &lt;code&gt;Event&lt;/code&gt; class. It allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create events with custom properties and methods&lt;/li&gt;
&lt;li&gt;Implement type-safe event handling&lt;/li&gt;
&lt;li&gt;Create reusable event classes across multiple components&lt;/li&gt;
&lt;li&gt;Add meaningful default options for events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you decide that creating custom event subclasses is not the approach you want to take, you can &lt;a href="https://dev.to/stuffbreaker/creating-strongly-typed-events-for-web-components-1jem"&gt;strongly type your events&lt;/a&gt; to create a good developer experience. &lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Custom Event Extension
&lt;/h2&gt;

&lt;p&gt;Let's start with a simple example. Here's how you might create a custom event for a shopping cart component:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CartItemAddedEvent&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="c1"&gt;// strongly type the event target&lt;/span&gt;
  &lt;span class="kr"&gt;declare&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;ShoppingCart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// define the parameters of the event using the constructor&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;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&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="c1"&gt;// pass the event name and any options to the base Event class&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;cart-item-added&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="c1"&gt;// initialize public property values&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;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&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;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;quantity&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;timestamp&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;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/** Get total price for added item */&lt;/span&gt;
  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&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;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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;quantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage in a web component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShoppingCart&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;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Add item to cart logic here...&lt;/span&gt;

    &lt;span class="c1"&gt;// Dispatch custom event&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;CartItemAddedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quantity&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Documenting Events
&lt;/h2&gt;

&lt;p&gt;To document these custom events, we can use the &lt;code&gt;@event&lt;/code&gt; or &lt;code&gt;@fires&lt;/code&gt; JSDoc tags in the component class's documentation. In the description, we can also provide a type. This information will all be added to the &lt;a href="https://dev.to/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0"&gt;Custom Element's Manifest&lt;/a&gt;. This is very helpful when creating custom integrations and type definitions for various environments.&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="cm"&gt;/**
 * ... other documentation
 *
 * @event {CartItemAddedEvent} cart-item-added - emitted when an item is added to the cart
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShoppingCart&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Global Event Map Registration
&lt;/h2&gt;

&lt;p&gt;To enable automatic type inference with &lt;code&gt;addEventListener&lt;/code&gt;, register your custom events in the global event map:&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="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;GlobalEventHandlersEventMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart-item-added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItemAddedEvent&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;h2&gt;
  
  
  Advanced Custom Event with Validation
&lt;/h2&gt;

&lt;p&gt;Here's a more sophisticated example of what can be done with custom events that includes validation and error handling:&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ValidationResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isValid&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="nl"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;FormValidationEvent&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="c1"&gt;// strongly type the event target&lt;/span&gt;
  &lt;span class="kr"&gt;declare&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;CustomForm&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;fieldName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ValidationResult&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;form-validation&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;false&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;fieldName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fieldName&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;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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationResult&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;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;createSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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;FormValidationEvent&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;FormValidationEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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="na"&gt;isValid&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;errors&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="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;createError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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;errors&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;FormValidationEvent&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;FormValidationEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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="na"&gt;isValid&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="na"&gt;errors&lt;/span&gt;&lt;span class="p"&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;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;errors&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;hasError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tooLong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invalidType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&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="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;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;errorType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getErrorMessage&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&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;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage in a form component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomForm&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;validateField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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;const&lt;/span&gt; &lt;span class="nx"&gt;validationResult&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;runValidation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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="c1"&gt;// using static methods to instantiate specific event variations&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="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt; 
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;FormValidationEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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="nx"&gt;FormValidationEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldName&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;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Event Hierarchies
&lt;/h2&gt;

&lt;p&gt;You can extend your custom events for complex component systems and set meaningful defaults in base classes to reduce repetitive 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="c1"&gt;// Base event class&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComponentEvent&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="kd"&gt;type&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="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="kd"&gt;type&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;span class="nx"&gt;options&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;timestamp&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;Date&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;componentId&lt;/span&gt; &lt;span class="o"&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;componentId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Specific event types&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ComponentLoadedEvent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ComponentEvent&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;componentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loadTime&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;component-loaded&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;componentId&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;loadTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loadTime&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;ComponentErrorEvent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ComponentEvent&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;componentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;component-error&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;componentId&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;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&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;severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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;get&lt;/span&gt; &lt;span class="nf"&gt;isCritical&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;severity&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;critical&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;h2&gt;
  
  
  Pros and Cons of Subclassing Events
&lt;/h2&gt;

&lt;p&gt;Like any engineering decision, there are trade-offs with choosing to go with a custom event class or using a standard &lt;code&gt;Event&lt;/code&gt; or &lt;code&gt;CustomEvent&lt;/code&gt;. Here are some pros and cons to subclassing &lt;code&gt;Event&lt;/code&gt; that teams should take into consideration when making a decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros of Extending the Event Class
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Type Safety and IntelliSense
&lt;/h4&gt;

&lt;p&gt;When using TypeScript or modern IDEs, extended Event classes provide excellent autocomplete and type checking without having to create new types.&lt;/p&gt;

&lt;h4&gt;
  
  
  Reusability
&lt;/h4&gt;

&lt;p&gt;Event classes can be imported and reused across multiple components, ensuring consistency and uniformity.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Logic
&lt;/h4&gt;

&lt;p&gt;You can add custom logic to your events rather than repeating it in your components.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rich API
&lt;/h4&gt;

&lt;p&gt;Custom methods and getters on your event classes provide a clean API for event consumers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Debugging
&lt;/h4&gt;

&lt;p&gt;Custom event classes make debugging easier with meaningful class names in stack traces.&lt;/p&gt;

&lt;h4&gt;
  
  
  Event Instance Checking
&lt;/h4&gt;

&lt;p&gt;Because the events are a specific class, developers can validate the event type by using &lt;code&gt;instanceof&lt;/code&gt;. This is very helpful if you are using event delegation by listening to events on parent elements.&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="nx"&gt;someDiv&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;my-event&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;MyCustomEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// logic for handling the event&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;
  
  
  Cons of Extending the Event Class
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Bundle Size
&lt;/h4&gt;

&lt;p&gt;Each custom event class adds to your JavaScript bundle size (though the impact is usually minimal).&lt;/p&gt;

&lt;h4&gt;
  
  
  Learning Curve
&lt;/h4&gt;

&lt;p&gt;Developers need to understand your custom event system, which adds complexity compared to standard DOM events.&lt;/p&gt;

&lt;h4&gt;
  
  
  Event Property Pollution
&lt;/h4&gt;

&lt;p&gt;When creating properties and methods on a custom event, those will be collocated with the standard &lt;code&gt;Event&lt;/code&gt; properties and methods. Teams may prefer having custom data located in a standard interface like the &lt;code&gt;detail&lt;/code&gt; property of a &lt;code&gt;CustomEvent&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Potential Overengineering
&lt;/h4&gt;

&lt;p&gt;It's easy to create overly complex event hierarchies that don't provide sufficient value.&lt;/p&gt;

&lt;h4&gt;
  
  
  Browser Compatibility
&lt;/h4&gt;

&lt;p&gt;While modern browsers support class extensions well, older browsers may encounter issues (although this is rarely a concern for modern web components in evergreen browsers).&lt;/p&gt;

&lt;h4&gt;
  
  
  Memory Overhead
&lt;/h4&gt;

&lt;p&gt;Creating many custom event instances can use more memory than simple object literals (though this is typically negligible).&lt;/p&gt;

&lt;h4&gt;
  
  
  Difficult to Create Abstractions
&lt;/h4&gt;

&lt;p&gt;If you are creating utilities for emitting events, it can be difficult since events will need to instantiate a custom event class. For example, a common approach with web component libraries is to create an &lt;code&gt;emit&lt;/code&gt; utility method in a component base class that can easily be used in inherited classes.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseComponent&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;emit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;T&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="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="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="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="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;composed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CustomClickDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;CustomButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseComponent&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="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="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="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;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;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="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Click Me&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// emit a custom event when the button is clicked&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CustomClickDetails&lt;/span&gt;&lt;span class="o"&gt;&amp;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;custom-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="na"&gt;message&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 was clicked!&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="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;button&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// emit a custom event when the component has loaded&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;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loaded&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;h2&gt;
  
  
  Things to Keep in Mind
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep it simple. Don't create custom events unless they add real value over &lt;code&gt;CustomEvent&lt;/code&gt; or &lt;code&gt;Event&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Include relevant data. Add properties that event listeners will need.&lt;/li&gt;
&lt;li&gt;Don't repeat properties and methods that the user can get from the event &lt;code&gt;target&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Provide meaningful default options for your events.&lt;/li&gt;
&lt;li&gt;Document your events. Provide clear documentation about when events fire and what data they contain.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Extending the JavaScript Event class is a powerful technique for creating rich, semantic event systems in web components. While it adds some complexity, the benefits of type safety, reusability, and clear APIs can outweigh the costs, especially in larger applications.&lt;/p&gt;

&lt;p&gt;Use this technique judiciously - not every interaction requires a custom event class. However, when you do need rich event data and behavior, extending &lt;code&gt;Event&lt;/code&gt; provides a clean and maintainable solution.&lt;/p&gt;

&lt;p&gt;If subclassing &lt;code&gt;Event&lt;/code&gt; looks like a good fit for you, start with simple extensions and gradually build more sophisticated event systems as your component library grows.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>html</category>
      <category>javascript</category>
      <category>events</category>
    </item>
    <item>
      <title>Creating Strongly Typed Events for Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Thu, 19 Jun 2025 12:14:16 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/creating-strongly-typed-events-for-web-components-1jem</link>
      <guid>https://forem.com/stuffbreaker/creating-strongly-typed-events-for-web-components-1jem</guid>
      <description>&lt;p&gt;If you've ever tried to handle custom events from web components in TypeScript, you've probably run into this frustrating error:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myElement&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;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;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;my-event&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="c1"&gt;// ❌ Property 'items' does not exist on type 'EventTarget'&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;target&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This article explores how to implement strongly typed custom events that enhance the developer experience while maintaining flexibility for non-TypeScript users.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fix TypeScript errors with custom events (5 minutes)&lt;/li&gt;
&lt;li&gt;Set up automatic type inference for your events&lt;/li&gt;
&lt;li&gt;Provide better event integration with React, Vue, and Angular&lt;/li&gt;
&lt;li&gt;Write self-documenting event code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Weakly Typed Event Information
&lt;/h2&gt;

&lt;p&gt;When working with custom events in web components, developers frequently need to access properties and methods from the element that emitted the event by accessing the event target. However, TypeScript doesn't automatically infer the correct type, leading to frustrating scenarios where type safety is lost at critical integration points.&lt;/p&gt;

&lt;p&gt;Consider this to-do list example:&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;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;completed&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&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="k"&gt;private&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;TodoItem&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;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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-ready&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;addItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt; &lt;span class="o"&gt;=&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;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;completed&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="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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="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-added&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="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&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;length&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;toggleItem&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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="nf"&gt;find&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="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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&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-toggled&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="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;total&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;length&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&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;_items&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;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When consuming this component, developers encounter type safety issues:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoList&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;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;todoList&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-added&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="c1"&gt;// ❌ Error: Property 'items' does not exist on type 'EventTarget'&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;target&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="c1"&gt;// ❌ Error: Property 'total' does not exist on type 'unknown'&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&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 typical workaround involves manual type assertions, which are verbose and error-prone:&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="nx"&gt;todoList&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-added&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="c1"&gt;// ⚠️ Requires type assertions&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;TodoList&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;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;target&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="c1"&gt;// ⚠️ Works, but fragile&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ⚠️ Requires knowledge of internal structure&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach creates several problems for library authors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poor Developer Experience: Users must remember to cast types manually&lt;/li&gt;
&lt;li&gt;Documentation Burden: You must document event structures separately&lt;/li&gt;
&lt;li&gt;Maintenance Overhead: Type mismatches cause runtime errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution: Strongly Typed Events
&lt;/h2&gt;

&lt;p&gt;The solution involves creating a generic type system that preserves both event target types and custom event detail types. This approach provides compile-time safety without any runtime overhead.&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="cm"&gt;/**
 * A generic type for strongly typing custom events with their targets
 * @template T - The type of the event target (extends EventTarget)
 * @template D - The type of the detail payload for the custom event
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TypedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;EventTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;CustomEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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;
  
  
  Defining Component Event Types
&lt;/h3&gt;

&lt;p&gt;For our &lt;code&gt;TodoList&lt;/code&gt; component, we can create strongly typed event definitions:&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="cm"&gt;/** Event detail type definition when the to-do list changes */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TodoChangeDetail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** The item that was added or changed */&lt;/span&gt;
  &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="cm"&gt;/** The total number of to-do items */&lt;/span&gt;
  &lt;span class="nl"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/** `TodoList` specific generic event type */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TodoListEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;unknown&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;TypedEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;D&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** The type for the change event */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TodoListEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoChangeDetail&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;h2&gt;
  
  
  Global Event Map Registration
&lt;/h2&gt;

&lt;p&gt;To enable automatic type inference with &lt;code&gt;addEventListener&lt;/code&gt;, register your custom events in the global event map:&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="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;GlobalEventHandlersEventMap&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-ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoListEvent&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-added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&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-toggled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&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;Now developers can use your events with full type safety:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoList&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;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;todoList&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-added&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="c1"&gt;// ✅ Fully typed - no assertions needed&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;target&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="c1"&gt;// TodoItem[]&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// number&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;todoList&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-toggled&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="c1"&gt;// ✅ Different detail type automatically inferred&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// boolean&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Non-unique Event Names
&lt;/h3&gt;

&lt;p&gt;When using standard event names like &lt;code&gt;change&lt;/code&gt; or &lt;code&gt;input&lt;/code&gt;, you cannot extend the global event map due to conflicts. Instead, provide typed overloads:&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="c1"&gt;// For standard events, provide explicit typing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoList&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;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;todoList&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;change&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="nx"&gt;TodoListChangeEvent&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="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="c1"&gt;// ✅ Strongly typed&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Documenting Events
&lt;/h2&gt;

&lt;p&gt;To document these custom events, we can use the &lt;code&gt;@event&lt;/code&gt; or &lt;code&gt;@fires&lt;/code&gt; JSDoc tags in the component class's documentation. In the description, we can also provide a type. This information will all be added to the &lt;a href="https://dev.to/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0"&gt;Custom Element's Manifest&lt;/a&gt;. This is very helpful when creating custom integrations and type definitions for various environments.&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="cm"&gt;/**
 * ... other documentation
 *
 * @event {TodoListEvent} todo-ready - emitted when the component is mounted
 * @event {TodoListChangeEvent} todo-added - emitted when a to-do item is added
 * @event {TodoListChangeEvent} todo-toggled - emitted when a to-do item is toggled
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TodoList&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;If you would like to play around with these types yourself, feel free to check out this &lt;a href="https://stackblitz.com/edit/vitejs-vite-s19auqdv?file=src%2Fmain.ts,src%2Ftodo-list.ts" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage in Frameworks
&lt;/h2&gt;

&lt;p&gt;Strongly typing these events can also make framework integration with your web components much easier. Here is a simple react example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TodoItem&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;./todo-list-types&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;TodoApp&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="kd"&gt;const&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;setItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TodoItem&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTotal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;handleTodoAdded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&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="nf"&gt;setItems&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="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="nf"&gt;setTotal&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;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&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;handleTodoToggled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TodoListChangeEvent&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="nf"&gt;setItems&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="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="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt; &lt;span class="na"&gt;ontodo-added&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleTodoAdded&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;ontodo-toggled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleTodoToggled&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Total items: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="na"&gt;textDecoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line-through&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;none&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;TodoApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benefits of Strongly Typed Custom Events
&lt;/h2&gt;

&lt;p&gt;This pattern provides several key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compile-time Safety: TypeScript catches mismatched property names and types before your code runs, preventing common runtime errors.&lt;/li&gt;
&lt;li&gt;Enhanced Developer Experience: IDE autocomplete works perfectly, showing available properties on both the event target and detail object.&lt;/li&gt;
&lt;li&gt;Self-Documenting Code: The type definitions serve as documentation, making it clear what data events are carried and which elements can dispatch them.&lt;/li&gt;
&lt;li&gt;Refactoring Confidence: When you change event data structures, TypeScript will flag all locations that need updates.&lt;/li&gt;
&lt;li&gt;Better Testing: Strongly typed events make it easier to write comprehensive tests since you know exactly what data to expect.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Strongly typing custom events transforms them from a potential source of bugs into a reliable, developer-friendly communication mechanism. By leveraging TypeScript's type system with patterns like the &lt;code&gt;TypedEvent&lt;/code&gt; we created, you can build more maintainable applications with better developer experience and fewer runtime surprises.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Finding What Drives You</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Mon, 13 Jan 2025 18:04:18 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/finding-what-drives-you-1jo</link>
      <guid>https://forem.com/stuffbreaker/finding-what-drives-you-1jo</guid>
      <description>&lt;p&gt;Every year there are predictions that "'x' will massively change 'y'" or "'a' will be the death of 'b'".&lt;/p&gt;

&lt;p&gt;AI continues to be a disruptor (for better or for worse) in many industries and one of the 2025 predictions is that "AI will be the death of the software engineer". As a software engineer, my knee-jerk reaction is to balk at the idea that AI could replace my craftsmanship. I love building software and AI appears to be positioned to threaten my livelihood and my passion. &lt;/p&gt;

&lt;p&gt;There's no putting the proverbial AI toothpaste back in the tube. It's here to stay and here to change things. Despite that, I don't think this is an accurate prediction, but my pragmatic side made me pause and think, "Well, what if...? Would I be ok? Would I be happy? Could I find a job that I enjoy as much as I like this?". To find some sense of safety in this, I've had to do a little self-reflection to understand why I love what I do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding My Drive
&lt;/h2&gt;

&lt;p&gt;After I graduated high school, I was in South Korea teaching an English lesson. One of the stories in the book we were using was the allegory "The King's Highway". It's short, so I've included it here for your viewing pleasure and to help provide context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  The King's Highway
&lt;/h3&gt;

&lt;p&gt;Once a king had a great highway built for the members of his kingdom. After it was completed, but before it was opened to the public, the king decided to have a contest. He invited as many as desired to participate.  Their challenge was to see who could travel the highway the best.&lt;/p&gt;

&lt;p&gt;On the day of the contest the people came. Some of them had fine chariots.  Some ran along the highway and others rode horses, mules, or donkeys.&lt;/p&gt;

&lt;p&gt;People traveled the highway all day, but each one, when he arrived at the end, complained to the king that there was a large pile of rocks and debris left on the road at one spot and this got in their way and hindered their travel.&lt;/p&gt;

&lt;p&gt;At the end of the day, a lone traveler wearily crossed the finish line and walked over to the king.  He was tired and dirty, but he addressed the king with great respect and handed him a bag of gold.  He explained, "I stopped along the way to clear a pile of rocks and debris that was blocking the road.  This bag of gold was under it all.  I want to give it to you so you can return it to its rightful owner."&lt;/p&gt;

&lt;p&gt;The king replied, "You are the rightful owner."&lt;/p&gt;

&lt;p&gt;The traveler replied, "Oh no, this is not mine.   I've never known such money."&lt;/p&gt;

&lt;p&gt;"Oh yes," said the king, "you've earned this gold, for you won my contest."&lt;/p&gt;

&lt;p&gt;"He who travels the road best is he who makes the road smoother for those who will follow."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The lesson in this story struck me deeply. It has stuck with me and I believe it has influenced my actions ever since. While I get paid as a software engineer, I don't get paid for any of my open-source projects, speaking events, or volunteering at hackathons. I'm not saying I'm above any of that, so if you'd like to pay me for those things, I'll gladly take your money. 😉 What I &lt;em&gt;am&lt;/em&gt; saying is that I would do these things whether I was getting paid or not because I like to help smooth the road for other travelers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this mean?
&lt;/h2&gt;

&lt;p&gt;Making the road smoother for others is a driving force for me, and software engineering is a mechanism for me to express that. I don't believe this is the sum of who I am, but this is a way I feel I can provide value. &lt;/p&gt;

&lt;p&gt;I have no plans to stop software engineering any time soon, but this helps me disassociate my identity from my profession. It now frees me to be open to the idea of growing beyond my trade and pivoting to something else if needed. This also allows me to feel less threatened by AI and opens me up to opportunities to embrace changes it may bring that could improve my existing experience.&lt;/p&gt;

&lt;p&gt;Find your drive and keep making cool shit!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Deprecating Your Web Component APIs</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Wed, 27 Nov 2024 19:06:53 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/deprecating-your-web-component-apis-3nh0</link>
      <guid>https://forem.com/stuffbreaker/deprecating-your-web-component-apis-3nh0</guid>
      <description>&lt;p&gt;Deprecating your web component APIs is a great way to communicate to your users that a feature will be removed in the next major release of your package. If you are using a &lt;a href="https://dev.to/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0"&gt;Custom Elements Manifest&lt;/a&gt; for documenting your component APIs, this may be trickier than it seems. Web components typically have more public APIs than framework components: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS variables&lt;/li&gt;
&lt;li&gt;CSS parts&lt;/li&gt;
&lt;li&gt;slots&lt;/li&gt;
&lt;li&gt;events&lt;/li&gt;
&lt;li&gt;methods&lt;/li&gt;
&lt;li&gt;attributes&lt;/li&gt;
&lt;li&gt;properties. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I will demonstrate how to document your API deprecations and add replacement features without introducing breaking changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;One of the trickiest parts of the deprecation process is the documentation. For properties, methods, and even your component, this can be as simple as adding a &lt;code&gt;@deprecated&lt;/code&gt; tag to your JSDoc comment in the component's class.&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="cm"&gt;/**
  * @deprecated This component is going away. Use ... instead.
  */&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;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/** @deprecated This property is being removed. Use ... instead. */&lt;/span&gt;
  &lt;span class="nx"&gt;myProp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/** @deprecated This method is going away. Use ... instead. */&lt;/span&gt;
  &lt;span class="nf"&gt;myMethod&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;The CSS variables, CSS parts, slots, events, and attributes are typically documented in the &lt;a href="https://custom-elements-manifest.open-wc.org/analyzer/getting-started/#supported-jsdoc" rel="noopener noreferrer"&gt;class's JSDoc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's an example of what you can document in a custom elements JSDoc:&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="cm"&gt;/**
 * @attr {boolean} disabled - disables the element
 * @attribute {string} foo - description for foo
 *
 * @csspart bar - Styles the bar element in the shadow DOM
 *
 * @slot - This is a default/unnamed slot
 * @slot container - You can put some elements here
 *
 * @cssprop --text-color - Controls the color of foo
 * @cssproperty [--background-color=red] - Controls the background color of bar
 *
 * @prop {boolean} prop1 - some description
 * @property {number} prop2 - some description
 *
 * @fires custom-event - some description for custom-event
 * @fires {MyType} typed-event - some description for typed-event
 * @event {MyType} typed-custom-event - some description for typed-custom-event
 *
 * @summary This is MyElement
 *
 * @tag my-element
 * @tagname my-element
 */&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;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;The challenge is that you can't double up on JSDoc tags and use the &lt;code&gt;@deprecated&lt;/code&gt; tag to indicate that these items are deprecated. Otherwise, it will be interpreted as the entire class is deprecated.&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="cm"&gt;/**
 * @cssprop --text-color - @deprecated I dub thee "deprecated" ❌
 */&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;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Solution
&lt;/h3&gt;

&lt;p&gt;To get around this, I created a tool (&lt;a href="https://www.npmjs.com/package/custom-elements-manifest-deprecator" rel="noopener noreferrer"&gt;custom-elements-manifest-deprecator&lt;/a&gt;) that allows you to tag items in your JSDoc so they are appropriately updated in the Custom Elements Manifest.&lt;/p&gt;

&lt;p&gt;Using this tool you can use alternative tags to identify deprecated APIs. By default, it uses a parenthesis-wrapped &lt;code&gt;@deprecated&lt;/code&gt; tag as the identifier (which is what we'll use today), but it can be customized to whatever you would 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="cm"&gt;/**
 * @cssprop --text-color - (@deprecated) I dub thee "deprecated" 👍
 */&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;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Updating Your APIs
&lt;/h2&gt;

&lt;p&gt;An important thing for our team is that when we will be removing or changing an API, we try to introduce the new feature before the next major release so teams can migrate to it early. That way, if they are staying up-to-date, they can minimize the impact of upgrading to a new version.&lt;/p&gt;

&lt;p&gt;In this next section, we will go through how to introduce new features without breaking your old APIs and how they can co-exist without competing with each other. We will be updating some APIs for a simple button component.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://stackblitz.com/edit/github-sxm7ap?file=src%2Fmy-element.ts%3AL26,custom-elements.json&amp;amp;title=%27CEM%20Deprecator%27" rel="noopener noreferrer"&gt;this example&lt;/a&gt; I will be using &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, but these features and principles can be applied to any environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CSS Variables
&lt;/h3&gt;

&lt;p&gt;To provide better autocomplete and descriptions in our editors like &lt;a href="https://www.npmjs.com/package/custom-element-vs-code-integration" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/custom-element-jet-brains-integration" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt;, we need to provide component-specific CSS variable names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* old variables */&lt;/span&gt;
&lt;span class="nt"&gt;--bg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ccc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--fg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c"&gt;/* new variables */&lt;/span&gt;
&lt;span class="nt"&gt;--button-bg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ccc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--button-fg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tricky part is that teams are already using the old variables, so we need them both to work. We can do this by mapping the deprecated variables to new ones and updating our button code to only use the variables. That way, if the users are styling with the deprecated variables, they will be applied to the new variables, or users can apply values to the new variables directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;--bg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;#ccc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--fg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;--button-bg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--bg-color&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;--button-fg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--fg-color&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--button-bg-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--button-fg-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--button-fg-color&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 can update our JSDoc information with the new CSS variables and add &lt;code&gt;(@deprecated)&lt;/code&gt; to the old ones with updated descriptions.&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="cm"&gt;/**
 * An example button element.
 *
 * @tag my-button
 * 
 * @cssprop [--bg-color=#ccc] - (@deprecated) (use `--button-bg-color` instead) controls the background color of the button
 * @cssprop [--fg-color=black] - (@deprecated) (use `--button-fg-color` instead) controls the foreground/text color of the button
 * @cssprop [--button-bg-color=#ccc] - controls the background color of the button
 * @cssprop [--button-fg-color=black] - controls the foreground/text color of the button
 *
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSS Parts
&lt;/h3&gt;

&lt;p&gt;Like our CSS variables, we want to provide namespaced names for our parts for better tooling support, so we will be replacing &lt;code&gt;control&lt;/code&gt; with &lt;code&gt;button-control&lt;/code&gt;. CSS parts are pretty easy since we can apply multiple parts to an element like CSS classes, so let's apply the new part name to the element alongside the other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;part=&lt;/span&gt;&lt;span class="s"&gt;"control button-control"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can update our JSDoc with the new part, deprecate the old part with &lt;code&gt;(@deprecated)&lt;/code&gt;, and update the description.&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="cm"&gt;/**
 * An example button element.
 *
 * @tag my-button
 *
 * @csspart control - (@deprecated) (use `button-control` instead) provides a hook to style internal button element
 * @csspart button-control - provides a hook to style internal button element
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Slots
&lt;/h3&gt;

&lt;p&gt;With the new initiatives for our components to support internationalization (i18n), we are updating some of our APIs to be more meaningful in RTL (right-to-left) languages. One thing we want to do is update our slot that is intended to display an icon before the button text from &lt;code&gt;left&lt;/code&gt; to &lt;code&gt;start&lt;/code&gt; without breaking the experience for projects that are already using the &lt;code&gt;left&lt;/code&gt; slot.&lt;/p&gt;

&lt;p&gt;We can do this by nesting the deprecated slot within the new slot. If the new slot is not being used, it will "fall back" to the old slot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;part=&lt;/span&gt;&lt;span class="s"&gt;"control"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"start"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can update our JSDoc with the new slot, deprecate the old slot with &lt;code&gt;(@deprecated)&lt;/code&gt;, and update the description.&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="cm"&gt;/**
 * An example button element.
 *
 * @tag my-button
 * 
 * @slot the default slot adds main content to the button
 * @slot left - (@deprecated) (use the `start` slot instead) adds content before the main button content
 * @slot start - adds content before the main button content
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Events
&lt;/h3&gt;

&lt;p&gt;For our example, we are emitting a custom &lt;code&gt;focus&lt;/code&gt; event, but it's getting confusing for teams, so we want to add a namespaced event (&lt;code&gt;my-focus&lt;/code&gt;). events are pretty straightforward since you can emit both events and developers can move to the new one when they get a chance.&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;private&lt;/span&gt; &lt;span class="nf"&gt;handleFocus&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="s2"&gt;focus&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="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-focus&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can update our JSDoc with the new event, deprecate the old event with &lt;code&gt;(@deprecated)&lt;/code&gt;, and update the description.&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="cm"&gt;/**
 * An example button element.
 *
 * @tag my-button
 * 
 * @event focus - (@deprecated) emitted when control is focused
 * @event my-focus - emitted when control is focused
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;NOTE: Most tools accept &lt;code&gt;@event&lt;/code&gt; and &lt;code&gt;@fires&lt;/code&gt; for documenting events. There isn't really a difference between them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Methods
&lt;/h3&gt;

&lt;p&gt;Methods are pretty easy to add in parallel to each other and you can use the standard &lt;code&gt;@deprecated&lt;/code&gt; tag in the method description to communicate that it's deprecated.&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="cm"&gt;/** @deprecated use `newMethod` instead */&lt;/span&gt;
&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;oldMethod&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="cm"&gt;/** description about this method */&lt;/span&gt;
&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;newMethod&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;h3&gt;
  
  
  Properties and Attributes
&lt;/h3&gt;

&lt;p&gt;Properties and attributes can be documented in the class's JSDoc using the &lt;code&gt;@attr&lt;/code&gt;/&lt;code&gt;@attribute&lt;/code&gt; and &lt;code&gt;@prop&lt;/code&gt;/&lt;code&gt;@property&lt;/code&gt; tags. If you are using them, you can use the &lt;code&gt;(@deprecated)&lt;/code&gt; tag to deprecate them in the Custom Elements Manifest, however, it's usually better to document the property directly using its own JSDoc comment. This will enable things like types and other tooling to properly identify deprecated APIs.&lt;/p&gt;

&lt;p&gt;The nice thing is that most analyzers are pretty good about associating attributes with properties that are being defined in the component class with decorators or other configurations, so if you deprecate the property, the associated attribute will also be deprecated.&lt;/p&gt;

&lt;p&gt;In our demo component, we have a &lt;code&gt;disable&lt;/code&gt; attribute that we want to update to &lt;code&gt;disabled&lt;/code&gt; to be more in line with native HTML elements.&lt;/p&gt;

&lt;p&gt;The first thing we want to do is deprecate the old property and add our new one.&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="cm"&gt;/** @deprecated Disables the button. */&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;Boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;disable&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="cm"&gt;/** Disables the button. */&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;Boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;disabled&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we want to avoid having to check both properties every time we need to determine if the component is disabled. To simplify this, we can convert the deprecated property to a getter/setter and use the new property to be the source of truth.&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="cm"&gt;/** @deprecated Disables the button. */&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;Boolean&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;disable&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="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;disabled&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;disable&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;boolean&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;disabled&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="cm"&gt;/** Disables the button. */&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;Boolean&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;disabled&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever the old value is updated, it will automatically update the new value, so we only need to check the new property to determine if the component is disabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt;&lt;span class="na"&gt;disabled=&lt;/span&gt;&lt;span class="s"&gt;${this.disabled}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the completed example!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/github-sxm7ap?file=src%2Fmy-element.ts%3AL26,custom-elements.json&amp;amp;title=%27CEM%20Deprecator%27" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Changing APIs can introduce complications, but it doesn't mean you need to stop producing new features because it may involve breaking changes. Introducing new features early and deprecating old ones can be a way to provide a good developer experience (DX). This provides a path of progressive enhancement rather than forcing teams to wait and make massive changes all at once to take advantage of new features.&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Do You Need to SSR Your Web Components?</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Fri, 22 Nov 2024 15:45:02 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/do-you-need-to-ssr-your-web-components-2oao</link>
      <guid>https://forem.com/stuffbreaker/do-you-need-to-ssr-your-web-components-2oao</guid>
      <description>&lt;p&gt;Web components seem to be picking up a lot of steam lately and there have been some concerns about their compatibility with client-side frameworks pre-rendering or server-side rendering client-side content on Node.js servers.&lt;/p&gt;

&lt;p&gt;There are ways to get your components working with SRRing, but is it worth actually SSRing your web components, or is client-side rendering enough? When exploring this question, the prevalent answer is the classic "it depends".&lt;/p&gt;

&lt;h2&gt;
  
  
  It Depends
&lt;/h2&gt;

&lt;p&gt;Two things to consider when deciding if and what you should be server-side rendering for web components.&lt;/p&gt;

&lt;p&gt;The first is, whether or not your web components have slow operations like data fetching. In this case, it may be a good idea to load some content from the server for the initial load.&lt;/p&gt;

&lt;p&gt;The second comes down to user experience and your user expectations. The benefits usually associated with SSRing your content are reduced dependency on JavaScript, improved SEO, and enhanced performance.&lt;/p&gt;

&lt;p&gt;There are many ways to use web components in your applications, but for this exploration, I will be focusing the discussion on "leaf node" primitives or small "dumb" components that receive data, and render some UI like those that would be found in a design system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduced JavaScript
&lt;/h2&gt;

&lt;p&gt;Web components can help reduce JavaScript dependency by removing dependencies on libraries and frameworks. If your framework is executing on the server, web components can be leveraged to handle the client-side user interactivity.&lt;/p&gt;

&lt;p&gt;An important note is that defining custom elements is part of the JavaScript DOM API, which means there is a JavaScript dependency. That may be a concern for some teams, but the good news is there are &lt;a href="https://dev.to/stuffbreaker/web-components-and-ssr-2024-edition-1nel"&gt;current and forthcoming ways&lt;/a&gt; to create them without JavaScript. &lt;/p&gt;

&lt;p&gt;The nice thing is that the JavaScript that is used to execute custom elements is extremely efficient which can make them &lt;em&gt;wicked&lt;/em&gt; fast! We'll discuss more about that in the performance section of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved SEO
&lt;/h2&gt;

&lt;p&gt;When content is server-side rendered, content can be pre-fetched and JavaScript can be pre-rendered which means web crawlers and SEO bots don't have to wait for your code to be loaded before indexing your content. They often won't wait for your asynchronous data to return before analyzing the page.&lt;/p&gt;

&lt;p&gt;Fortunately, client-side rendered web components work fine for SEO, including content rendered in the shadow DOM. You can read more about it in an &lt;a href="https://dev.to/stuffbreaker/seo-and-web-components-2023-edition-3l6i"&gt;article I wrote last year&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved Performance
&lt;/h2&gt;

&lt;p&gt;One of the nice things about web components is that they are parsed and rendered directly by the browser rather than having a library parse and render the UI (again, &lt;a href="https://krausest.github.io/js-framework-benchmark/2024/table_chrome_129.0.6668.58.html" rel="noopener noreferrer"&gt;wicked fast&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Also, isolating content in the shadow DOM allows CSS and JavaScript selectors to perform more efficiently both in and out of your custom elements. Using selectors within your component allows them to stay scoped within the shadow DOM rather than querying the entire document for elements that match the parameters. Likewise, global styles and scripts skip over shadow DOM content which reduces the number of elements they need to query.&lt;/p&gt;

&lt;p&gt;I created a &lt;a href="https://break-stuff.github.io/wc-fouc-test/" rel="noopener noreferrer"&gt;simple example&lt;/a&gt; with a bunch of &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt; components where they are being &lt;em&gt;lazy-loaded from a CDN&lt;/em&gt;. I loaded the components this way to show worst-case-scenario loading performance. As you can see, it still loads quite quickly. &lt;/p&gt;

&lt;p&gt;After many, &lt;em&gt;many&lt;/em&gt; attempts, the worst score I could get from Lighthouse on performance was a 94 (part of this may have been from too many people watching Netflix at my house affecting CDN download times).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fury054tydy1ynd4wy4nk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fury054tydy1ynd4wy4nk.png" alt="a screenshot of a lighthouse score of 94 where cumulative layout shift was not good" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If these were being eager-loaded from the same server, the performance would be consistently better! &lt;/p&gt;

&lt;h2&gt;
  
  
  But What About FOUC???
&lt;/h2&gt;

&lt;p&gt;I believe FOUC (flash of unstyled content) is a legitimate concern for web components and should be something teams should take into consideration, especially for things like layout components. While the component loads and applies the styles and JavaScript content within and around them can shift which can negatively affect the user experience. &lt;/p&gt;

&lt;p&gt;As you can see from the example above, the reason for the lower score was due to the &lt;a href="https://web.dev/articles/cls" rel="noopener noreferrer"&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; that occurs as the components load and render. Again, the components are being lazy-loaded from a CDN, so this will be slightly exaggerated from some typical implementations, which helps emphasize the point.&lt;/p&gt;

&lt;p&gt;An approach I have commonly used to reduce this is to use CSS to hide elements that have yet to be defined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:defined&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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 works better than &lt;code&gt;display: none;&lt;/code&gt; because it allows the component to continue to occupy space while it loads and shifts around, which can reduce layout shifting, but it's not great. Then I came across &lt;a href="https://www.abeautifulsite.net/posts/flash-of-undefined-custom-elements/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; by Cory LaViska that goes into greater detail about working with custom elements and FOUC as well as providing alternative solutions. Based on this and after discussing it with others, I have settled on this solution for now.&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;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.wc-loaded&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:not(:defined)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;component&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;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;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;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="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="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wc-loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Add fallback in case a component fails to load&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wc-loaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very simple solution that can be tweaked and modified to meet your environment's needs. You can see how it smooths out the load experience in &lt;a href="https://break-stuff.github.io/wc-fouc-test/without_fouc.html" rel="noopener noreferrer"&gt;this example&lt;/a&gt;. Also, it's improved our Lighthouse score!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F8an11tf90z3yha0s211v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F8an11tf90z3yha0s211v.png" alt="a screenshot of a perfect performance score in Lighthouse" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;So after all this, the million-dollar question is do teams need to SSR their web components? The answer &lt;em&gt;really&lt;/em&gt; is "it depends" and this information should be evaluated based on your application and your user's needs. &lt;/p&gt;

&lt;p&gt;But, for most teams using web components for their design systems and other "leaf nodes" in their projects, it's probably not worth it. They already run very lean JavaScript, are tuned for performance, and work fine for SEO, so the benefits gained by trying to SSR your components may be trivial in comparison to the work it takes to get them working for each framework.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>ssr</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Web Components and SSR - 2024 Edition</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Tue, 19 Nov 2024 14:02:30 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/web-components-and-ssr-2024-edition-1nel</link>
      <guid>https://forem.com/stuffbreaker/web-components-and-ssr-2024-edition-1nel</guid>
      <description>&lt;p&gt;One of the biggest gripes I hear about web components is that they don't work with Server-Side Rendering. This is unfortunately a little misleading because it's only partially true. You see, there's server-side rendering and there's server-side rendering. It works in the former, but not the latter. Don't worry, I'll explain. 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;There are really two kinds of server-side rendering that get conflated into the same concept. The first is server-side framework server-side rendering and client-side framework server-side rendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-Side Framework SSR
&lt;/h3&gt;

&lt;p&gt;Server-side Framework SSR is when you use a framework that runs the HTML templating logic entirely on the server to compose the HTML that will be rendered in the browser. These are frameworks like &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/tutorials/choose-web-ui" rel="noopener noreferrer"&gt;ASP.Net&lt;/a&gt;, &lt;a href="https://www.php.net/manual/en/faq.html.php" rel="noopener noreferrer"&gt;PHP&lt;/a&gt;, or even Node.js frameworks that use templating languages like &lt;a href="https://pugjs.org/api/getting-started.html" rel="noopener noreferrer"&gt;Pug&lt;/a&gt; or &lt;a href="https://ejs.co/" rel="noopener noreferrer"&gt;EJS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Web components work great in this type of server-side rendering!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Framework SSR
&lt;/h3&gt;

&lt;p&gt;Client-side Framework SSR is when a frontend framework executes the client-side code on a Node.js server before sending it to the browser. This is done to reduce the amount of JavaScript that is shipped and executed in the browser. &lt;/p&gt;

&lt;p&gt;There are some big advantages of doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;improve performance&lt;/li&gt;
&lt;li&gt;better SEO&lt;/li&gt;
&lt;li&gt;reduced dependency on JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The problem is that this approach introduces some challenges for custom elements and other web APIs.&lt;/p&gt;

&lt;p&gt;The first challenge is that when defining custom elements the API is &lt;code&gt;window.customElements.define('my-element', MyElement)&lt;/code&gt;. When you render client-side code on a Node.js server, guess what's missing - the &lt;code&gt;window&lt;/code&gt;. Because of this, typical commands on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window" rel="noopener noreferrer"&gt;&lt;code&gt;window&lt;/code&gt; object&lt;/a&gt;, and other APIs like &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;MutationObserver&lt;/code&gt; fail during SSRing.&lt;/p&gt;

&lt;p&gt;The other challenge is that web components are interoperable, which means they can be used across frameworks like standard HTML elements, but client-side framework SSRing is not based on any standard. Each framework has its own bespoke method of executing its UI, so there is no clear approach for how and when to SSR custom elements. You need an implementation for each environment in which they are being used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Solutions
&lt;/h2&gt;

&lt;p&gt;You can do some things right now to use your web components with client-side framework SSRing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deferred Definition
&lt;/h3&gt;

&lt;p&gt;The first thing you can do to get your components working right now is defer the component definition until the &lt;code&gt;window&lt;/code&gt; object is present. Unfortunately, checking if the &lt;code&gt;window&lt;/code&gt; exists before defining your components won't consistently work because the logic will be executed on the server and may not be run again when the code gets to the client. Most frameworks have a way to specify when code needs to be run on the "client only", so you will need to find out how to do that based on the framework you're using. &lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative Shadow DOM (DSD)
&lt;/h3&gt;

&lt;p&gt;A recent development has been the ability to define HTML templates in a shadow root declaratively. This has been dubbed &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html" rel="noopener noreferrer"&gt;declarative shadow DOM&lt;/a&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;my-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;
  My Button
&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides some amazing capabilities and if you defer the component definition, they can be upgraded when the client is ready. The downside is that it doesn't scale very well for something like design systems with many components. You would need to do this for every custom element on the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;
  My Button 1
&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;shadowrootmode=&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;
  My Button 2
&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Teams are attempting to use this as a stop-gap for now (like &lt;a href="https://www.npmjs.com/package/@lit-labs/ssr" rel="noopener noreferrer"&gt;@lit-labs/ssr&lt;/a&gt;). This often requires special code considerations like limitations on how and when you can use certain APIs, so I don't think this is an ideal solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  WASM?
&lt;/h3&gt;

&lt;p&gt;An interesting new solution from the &lt;a href="https://enhance.dev/" rel="noopener noreferrer"&gt;Enhance&lt;/a&gt; team is that they are using &lt;a href="https://enhance.dev/wasm" rel="noopener noreferrer"&gt;WebAssembly to provide SSRing of custom elements&lt;/a&gt;. This is currently limited to their ecosystem, but there may be an opportunity to learn from this and try to create a more framework-agnostic approach in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Solutions
&lt;/h2&gt;

&lt;p&gt;There are some new proposals in the works that should provide a scalable solution to some of the pain points SSRing is trying to solve when it comes to web components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declarative Custom Elements (DCE)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/w3c/webcomponents-cg/issues/32" rel="noopener noreferrer"&gt;Declarative Custom Elements (DCE)&lt;/a&gt; is a technology that may provide a more efficient approach to authoring web components. These are similar to custom elements using the Declarative Shadow DOM, but you only have to define them once and they can be used everywhere.&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;template&lt;/span&gt; &lt;span class="na"&gt;element=&lt;/span&gt;&lt;span class="s"&gt;"my-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/style&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;slot&amp;gt;&amp;lt;/slot&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;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;My Button 1&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;My Button 2&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, in conjunction with &lt;a href="https://github.com/WICG/webcomponents/blob/gh-pages/proposals/html-modules-explainer.md" rel="noopener noreferrer"&gt;HTML modules&lt;/a&gt; would provide a mechanism for custom elements that are both client-side framework SSR-friendly, reduce the dependency on JavaScript, and improve the performance of our custom elements that don't require any JavaScript. Also, like custom elements using the Declarative Shadow DOM, these custom elements can be upgraded once the client is available for more advanced user interactions.&lt;/p&gt;

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

&lt;p&gt;If your application uses a server-side framework to render your UIs, you are safe to use web components. If you are using a client-side framework to render your UIs, there are some things you can do now to make your components work in those scenarios, but some new things are coming that should greatly improve the experience SSR for custom elements.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>ssr</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Bulletproof Web Component APIs</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Tue, 12 Nov 2024 13:27:08 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/bullet-proof-web-component-apis-okd</link>
      <guid>https://forem.com/stuffbreaker/bullet-proof-web-component-apis-okd</guid>
      <description>&lt;p&gt;Web components/custom elements provide some great features that can make your UX more efficient and scalable, but there are some "gotchas" that can prevent teams from having a good experience with your components.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;One of the great things about custom elements/web components can sometimes be a challenge for them - &lt;em&gt;they can be used anywhere&lt;/em&gt;. You never know if they're being used in a framework, in a type-safe environment, rendered on the server with a language like PHP, created programmatically using JavaScript's &lt;code&gt;createElement&lt;/code&gt; function, or even in plain ol' HTML. &lt;/p&gt;

&lt;p&gt;The challenge is that there's no consistent way to ensure your web component APIs are correctly implemented. Because of this, one of the items in our component library's PR checklist is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Attributes and properties work when set, unset, and poorly set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, in our library, we have an "input" component that, like a native &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element, has a &lt;code&gt;type&lt;/code&gt; attribute with some specified values. It doesn't have all of the same options because we have specific components for some of the other controls like radios and checkboxes.&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="cm"&gt;/** A string specifying the type of control to render. */&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="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; The code examples are using &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, but the principles discussed here can be applied to other libraries and frameworks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we test testing this attribute, we get inconsistent results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set 

&lt;ul&gt;
&lt;li&gt;everything works as expected.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&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;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"tel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Unset 

&lt;ul&gt;
&lt;li&gt;the component works fine when the attribute is not set because of the default value&lt;/li&gt;
&lt;li&gt;the component renders correctly when the property is &lt;code&gt;undefined&lt;/code&gt; because the internal HTML element is resilient, but the custom logic and validation in the component break
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// When the attribute is not set
&lt;span class="nt"&gt;&amp;lt;my-input&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;

// When the property is `undefined` (example using JSX)
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;{undefined}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Poorly set 

&lt;ul&gt;
&lt;li&gt;setting the &lt;code&gt;type&lt;/code&gt; attribute value to "rubbish" renders a text input, but also breaks our custom logic and validation. &lt;/li&gt;
&lt;li&gt;setting it to a value that is a valid HTML input type, but not one that we have specified for the component, renders controls not intended for our component which not only breaks our custom logic and validation, but also our styles/designs.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- not a real type --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rubbish"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- we don't want users using this type --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/my-input&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test this example here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/vitejs-vite-pnfzwv?file=src%2Fmy-input.ts,index.html" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we fix it?
&lt;/h2&gt;

&lt;p&gt;I noticed that the native HTML element seems to pass the "set, unset, and poorly set" test, so let's see if we can learn from it.&lt;/p&gt;

&lt;p&gt;When I poorly set the native input's attribute and log the property values, I can see why it works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- set the value to a non-standard type --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"rubbish"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"undefinedInput"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// programmatically set the value to `undefined`&lt;/span&gt;
  &lt;span class="nx"&gt;undefinedInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&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;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;rubbishInput&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;// logs 'text'&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;undefinedInput&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;// logs 'text'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an invalid value is assigned to the attribute or property, it falls back to a default value. We should be able to do the same and still maintain strong typing.&lt;/p&gt;

&lt;p&gt;Let's start by creating a list of valid values and a type for our 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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;color&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;date&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;email&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;number&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;password&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;search&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;tel&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;text&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;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use the array to create a union type for TypeScript validation.&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InputType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;inputTypes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;number&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 can update our custom elements property with some validation logic. We can do this by converting our existing property to a standard &lt;a href="https://www.javascripttutorial.net/javascript-getters-and-setters/" rel="noopener noreferrer"&gt;JavaScript class getter and setter&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="c1"&gt;// private property for storing the `type` property value&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InputType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** A string specifying the type of control to render. */&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="kd"&gt;set&lt;/span&gt; &lt;span class="nf"&gt;type&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;InputType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Confirming the value is in our approved list of values. If not, it will use a fallback value of "text"&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;_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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;get&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;InputType&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;_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's our final output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/vitejs-vite-rhr4vn?file=src%2Fmy-input.ts,index.html" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With this new validation in place, our input component is far more resilient than before and also allows for more complex validation if necessary. This method may also be overkill for some of your attributes, especially those that are for styling. For those scenarios, be sure to check out &lt;a href="https://dev.to/stuffbreaker/dont-rely-on-default-attribute-values-for-styling-web-components-1i1k"&gt;this article&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webcomponents</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Don't Rely on Default Attribute Values For Styling Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Tue, 12 Nov 2024 13:26:48 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/dont-rely-on-default-attribute-values-for-styling-web-components-1i1k</link>
      <guid>https://forem.com/stuffbreaker/dont-rely-on-default-attribute-values-for-styling-web-components-1i1k</guid>
      <description>&lt;p&gt;Don't get me wrong, I have nothing against default values for web component APIs. The problem I have with them is that they are unreliable. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;A common approach for providing a list of available options for an API is using TypeScript's untion type.&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="cm"&gt;/** The display variant for the button */&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;reflect&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;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ghost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&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;Here is some basic CSS to make the variations work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0265dc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'default'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'solid'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'ghost'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; The code examples are using &lt;a href="https://lit.dev/" rel="noopener noreferrer"&gt;Lit&lt;/a&gt;, but the principles discussed here can be applied to other libraries and frameworks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The challenge is custom elements/web components can be used anywhere. They can be inserted in the DOM in strings, in server-side languages like PHP, they can be created in JavaScript's &lt;code&gt;createElement&lt;/code&gt; function, or even in standard HTML. What I'm getting at is that there is not always a "type-safe" way to ensure custom element attributes are being set accurately. Because of this, one of the items in our component library's PR checklist is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ Attributes and properties work when set, unset, and poorly set.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing Our API
&lt;/h2&gt;

&lt;p&gt;Given these guidelines, let's test the API setup above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set - everything looks good.
&lt;/li&gt;
&lt;/ul&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;my-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"default"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Default Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"solid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Solid Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"ghost"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2F5uhds3e0qckw2j6ulck6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5uhds3e0qckw2j6ulck6.png" alt="Screenshot of the three button variations" width="654" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unset 

&lt;ul&gt;
&lt;li&gt;without an attribute set it works fine because we have a default value and it is configured to reflect the attribute on the element when it is set.&lt;/li&gt;
&lt;li&gt;if we set the &lt;code&gt;variant&lt;/code&gt; property to &lt;code&gt;undefined&lt;/code&gt;, it breaks the styles.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- No attribute set --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-button&amp;gt;&lt;/span&gt;No Attribute Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- JSX example --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;my-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;{undefined}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Unset Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2F6uzvuv1jlfmvmbol9a5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F6uzvuv1jlfmvmbol9a5f.png" alt="button missing styles when variant attribute is undefined" width="240" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poorly set - when we set the &lt;code&gt;variant&lt;/code&gt; attribute to "rubbish" it also breaks.
&lt;/li&gt;
&lt;/ul&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;my-button&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"rubbish"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rubbish Button&lt;span class="nt"&gt;&amp;lt;/my-button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.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%2F5r6umajkwpu9qutrfrdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5r6umajkwpu9qutrfrdr.png" alt="button missing styles when the variant attribute is set to rubbish" width="274" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can test this example here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/vitejs-vite-oxefex?file=src%2Fmy-element.ts" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixing the API
&lt;/h2&gt;

&lt;p&gt;The easiest way to fix this is to make the &lt;code&gt;button&lt;/code&gt; element styles match the default styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5rem&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 can remove the code for the default variation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* We can remove this */&lt;/span&gt;
&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'default'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--accent-color&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;To avoid confusion, you can leave the style and add a comment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Styles for this variant are under the `button` element */&lt;/span&gt;
&lt;span class="nd"&gt;:host&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="nt"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'default'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also update the TypeScript API to make it optional and remove the default value.&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="cm"&gt;/** The display variant for the button */&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;reflect&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;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;solid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ghost&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;The elements now behave consistently if the value is set, unset, or poorly set!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fuogvvgcyvhsn93avfumb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fuogvvgcyvhsn93avfumb.png" alt="all buttons displaying correctly event when not configured correctly" width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see the final code here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/edit/vitejs-vite-em411y?file=src%2Fmy-element.ts" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;By removing a dependency on default values, you can create more resilient web component APIs. If you must have default values for your components to function properly, be sure to check out &lt;a href="https://dev.to/stuffbreaker/bullet-proof-web-component-apis-okd"&gt;this article&lt;/a&gt; to create web components that work consistently.&lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>You Should Be Shipping a Manifest with Your Web Components</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Thu, 07 Nov 2024 13:48:16 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0</link>
      <guid>https://forem.com/stuffbreaker/you-should-be-shipping-a-manifest-with-your-web-components-2da0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Besides your components, the Custom Elements Manifest is the most important thing you can ship in your library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is a Custom Elements Manifest (CEM)?
&lt;/h2&gt;

&lt;p&gt;The Custom Elements Manifest is a &lt;a href="https://github.com/webcomponents/custom-elements-manifest" rel="noopener noreferrer"&gt;schema&lt;/a&gt; designed to document the metadata about your custom elements/web components, including attributes, properties, methods, events, slots, CSS parts, and CSS variables. It takes all of the information about your components and serializes it into a single &lt;code&gt;json&lt;/code&gt; file in your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do users need it?
&lt;/h2&gt;

&lt;p&gt;This standardized documentation method unlocks huge possibilities with how teams use and interact with your component library. Developers can use it for documentation purposes like &lt;a href="https://opensource.adobe.com/spectrum-web-components/components/badge/api/" rel="noopener noreferrer"&gt;Adobe Spectrum's API documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F1nmyztitzo8x5rbkxvdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F1nmyztitzo8x5rbkxvdd.png" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Teams can also use them for framework, IDE integrations, and other tooling like &lt;a href="https://www.npmjs.com/package/wc-storybook-helpers" rel="noopener noreferrer"&gt;Storybook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is nice if you want to create specific types or framework integrations you would like to ship with your components, but it's tough to anticipate all of your user's needs. You may be building your components to be used in a Vue.js environment, but another team may come along that needs to use your components in a react environment. Rather than waiting for you to build and ship react wrappers, teams can use the &lt;a href="https://www.npmjs.com/package/custom-element-react-wrappers" rel="noopener noreferrer"&gt;CEM to generate their own wrappers&lt;/a&gt; locally.&lt;/p&gt;

&lt;p&gt;A recent example of this was when I was helping a team get up and running with &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt; in a Next.js application. Shoelace provides react wrappers, but they were throwing an error when Next.js tried to server-side render them. Fortunately, Shoelace ships their CEM, so I was able to use it to generate new wrappers that were SSR-safe.&lt;/p&gt;

&lt;p&gt;Here is a link to an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackblitz.com/github/break-stuff/shoelace-nextjs?file=README.md" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdeveloper.stackblitz.com%2Fimg%2Fopen_in_stackblitz.svg" alt="Open in StackBlitz" width="162" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you create a CEM?
&lt;/h2&gt;

&lt;p&gt;There are a couple of tools out there for creating a CEM (&lt;a href="https://www.npmjs.com/package/web-component-analyzer" rel="noopener noreferrer"&gt;web-component-analyzer&lt;/a&gt; and Lit labs has an &lt;a href="https://www.npmjs.com/package/@lit-labs/analyzer" rel="noopener noreferrer"&gt;experimental tool&lt;/a&gt;), but my go-to tool is the &lt;a href="https://custom-elements-manifest.open-wc.org/analyzer/getting-started/" rel="noopener noreferrer"&gt;Custom Elements Manifest Analyzer&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This is a great option for a few reasons reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It supports &lt;a href="https://custom-elements-manifest.open-wc.org/analyzer/getting-started/#support" rel="noopener noreferrer"&gt;multiple frameworks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It has a great &lt;a href="https://custom-elements-manifest.open-wc.org/analyzer/plugins/intro/" rel="noopener noreferrer"&gt;plugin system&lt;/a&gt; for developers to extend the functionality of the analyzer&lt;/li&gt;
&lt;li&gt;Not only is it easy to use, but it also has great documentation and &lt;a href="https://discord.gg/Svzr5bZ9" rel="noopener noreferrer"&gt;community support&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some available plugins I've created that can help improve your custom element adoption:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;IDE Integrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-vs-code-integration" rel="noopener noreferrer"&gt;VS Code Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-jet-brains-integration" rel="noopener noreferrer"&gt;JetBrains IDE Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;JS Framework Integrations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-react-wrappers" rel="noopener noreferrer"&gt;React Wrappers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-vuejs-integration" rel="noopener noreferrer"&gt;Vue.js Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-solidjs-integration" rel="noopener noreferrer"&gt;Solid.js Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-svelte-integration" rel="noopener noreferrer"&gt;Svelte Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/custom-element-jsx-integration" rel="noopener noreferrer"&gt;JSX Types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; These provide CEM Analyzer plugins and functions for pre-generated CEMs. If you're not using the CEM Analyzer, don't worry, you can still take advantage of these.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;The Custom Element Manifest is a great tool for accelerating user adoption of your custom element component library. By providing it as part of your product, you can provide consumers with the means to ensure their needs are being met when using your custom elements. &lt;/p&gt;

&lt;p&gt;When choosing a library or framework for authoring your custom elements, it's a good idea to try to find one that you can generate a CEM, especially if your components will be used by other teams. &lt;/p&gt;

</description>
      <category>html</category>
      <category>css</category>
      <category>javascript</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Dark Mode in Web Components is About to Get AWESOME!</title>
      <dc:creator>Burton Smith</dc:creator>
      <pubDate>Wed, 17 Apr 2024 01:29:05 +0000</pubDate>
      <link>https://forem.com/stuffbreaker/dark-mode-in-web-components-is-about-to-get-awesome-4i14</link>
      <guid>https://forem.com/stuffbreaker/dark-mode-in-web-components-is-about-to-get-awesome-4i14</guid>
      <description>&lt;p&gt;You may think the title of this article is hyperbole, but I assure you what is coming for managing "dark mode" in web components is about to get awesome. Before we get into the solutions, let's talk about the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When working with light and dark mode themes today we typically have two approaches to solving the problem - using media queries and using a theme switcher/toggle. &lt;/p&gt;

&lt;h3&gt;
  
  
  Using Media Queries
&lt;/h3&gt;

&lt;p&gt;The first is with the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query and CSS custom properties. Colors and styles are defined one way for "light mode" and again within a media query for "dark mode".&lt;/p&gt;

&lt;p&gt;This method allows us to provide styles based on the user's system preferences, but leads to large CSS files and makes it difficult to manage the theme globally or at a component level (for example, if we wanted to create an "inverted" nav bar) because it's difficult to force the color scheme to change.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/YzMvzVY?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; In addition to the many benefits of CSS custom properties, it is important to note that they are inheritable across shadow roots, which makes them fantastic tools for managing design tokens in a web component library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using a Theme Switcher
&lt;/h3&gt;

&lt;p&gt;Another scenario is when sites provide a theme toggle so users can choose between light and dark modes, but also allow it to fall back to user preferences if one isn't selected. This usually requires defining styles scoped to "light-mode" and "dark-mode" class names and some JavaScript to check the user's system preferences to set the user hasn't been specified.&lt;/p&gt;

&lt;p&gt;This can introduce FOWC - Flash of Wrongly-styled Content (yes, I just made that up) - while the browser loads and parses the JavaScript. It also makes it very inconvenient to understand what "mode" our project is in within the shadow DOM. &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/poBKoLv?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;light-dark()&lt;/code&gt; CSS Function
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;light-dark()&lt;/code&gt; is a new CSS color function that allows us to define light and dark mode colors in a single property without needing to redefine our properties in a media query or alternative class name. With this new function, we can simplify the implementation by not having to define the style in two different places.&lt;/p&gt;

&lt;p&gt;This example provides the same functionality as the first example with the media query, but with significantly less code and is much easier to maintain.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/zYXLOVg?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If we use a design token system, this can be used with CSS custom properties (variables) to create other CSS custom properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;--button-bg-color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;light-dark&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--light-color&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--dark-color&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using &lt;code&gt;color-scheme&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If we want to add the theme switcher capability, we can do so with just a few lines of CSS. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme" rel="noopener noreferrer"&gt;CSS &lt;code&gt;color-scheme&lt;/code&gt; property&lt;/a&gt; allows us to dictate to the browser which color scheme to use. This is nice because it not only uses the light and dark colors defined in the &lt;code&gt;light-dark()&lt;/code&gt; function but also the browser's native light and dark mode colors.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/ZEZjEEZ?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Composing Web Components
&lt;/h2&gt;

&lt;p&gt;Let's add a web component into the mix and see what happens. In this example, the web component requires different colors than the global button colors we originally defined. &lt;/p&gt;

&lt;p&gt;One of the best parts about this is that the &lt;code&gt;color-scheme&lt;/code&gt; value is &lt;em&gt;inheritable across the shadow root&lt;/em&gt;, meaning the code in our shadow DOM can be context-aware of the color scheme!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/gOyjOpX?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Themable Components
&lt;/h2&gt;

&lt;p&gt;Not only can the components be aware of the color scheme, but we can also dictate it based on the component's API.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/break-stuff/embed/qBwMNoN?height=600&amp;amp;default-tab=css,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;p&gt;At the time of writing this article, the &lt;code&gt;light-dark()&lt;/code&gt; function is in the beta release of &lt;em&gt;Safari&lt;/em&gt;. It also looks like the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark#browser_compatibility" rel="noopener noreferrer"&gt;MDN browser compatibility info&lt;/a&gt; and &lt;a href="https://caniuse.com/?search=light-dark" rel="noopener noreferrer"&gt;caniuse.com&lt;/a&gt; are not accurate. These work in both &lt;em&gt;Edge&lt;/em&gt; and &lt;em&gt;Opera&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fut2d42kjzr6u6pdyd9ee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fut2d42kjzr6u6pdyd9ee.png" alt="screenshot of MDN docs where it shows browser incompatibility for Edge and Opera" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;With these new CSS features, our ability to style content in and outside our web components will significantly improve. We can get more features than we have today with much less code that is more performant and compatible with the native browser APIs.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webcomponents</category>
      <category>html</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
