<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Warren</title>
    <description>The latest articles on DEV Community by Michael Warren (@michaelwarren1106).</description>
    <link>https://dev.to/michaelwarren1106</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%2F724519%2Fea8c45d3-5c7f-4045-895f-47ec04925f11.png</url>
      <title>DEV Community: Michael Warren</title>
      <link>https://dev.to/michaelwarren1106</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaelwarren1106"/>
    <language>en</language>
    <item>
      <title>Why is Visual Difference Testing still so hard?</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Wed, 25 Mar 2026 04:42:09 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/why-is-visual-difference-testing-still-so-hard-ad2</link>
      <guid>https://dev.to/michaelwarren1106/why-is-visual-difference-testing-still-so-hard-ad2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post is going to be half rant and half educational. At least that‘s what I'm aiming for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is VDT?
&lt;/h2&gt;

&lt;p&gt;The concept goes by a few names, but is essentially the same regardless of terminology. You might have heard of “Visual Regression Testing” (VRT) or ”Visual Difference Testing” (VDT). It might be called something else in your sphere, but the idea is the same. Test the visual parts of your application so that when/if anything has visually changed, you can do something about it. I like to call it VDT because I think of the practice as hunting for differences between the current state of a UI and the new state. The difference might not be a regression, it might be a purposeful change or even an acknowledged cross-browser difference like focus states on buttons between Chromium and Webkit.&lt;/p&gt;

&lt;p&gt;The most common practice—and I think maybe the actual only way to do it—is to render the part of your UI you want to test, take a screenshot of that scenario and save it. Then on your next test run, take the same screenshot of the same scenario again and compare the old screenshot against the new one somehow. There are a few ways to compare. I’ve seen methods that involve setting each screenshot to have a lot of transparency and overlaying the two on top of one another to see where differences might be. The most common method seems to be a pixel-by-pixel analysis of each screenshot and logging where there are differences. Some of the pixel tools are the old school deterministic route, and some of the newer ones are “AI-driven” because of course they are. Can’t build any kind of tool these days without sloppin’ some good ’ole AI on it somehow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we need VDT anyway?
&lt;/h2&gt;

&lt;p&gt;You know why a lot of devs say they hate CSS? Because they don‘t really understand what it does and don’t really know the ramifications of making stylistic changes in their UIs? Well VDT helps devs react to those visual changes. If you test what it was before you changed, and test what it looks like after you change it, then ostensibly you can see what you changed, decide what to do about it and whether or not to continue with your desired approach. Like any testing, VDT is very useful in not releasing buggy UIs, except the difference is that the bugs are visual not logical.&lt;/p&gt;

&lt;h3&gt;
  
  
  VDT has different use &amp;amp; importance depending on your product
&lt;/h3&gt;

&lt;p&gt;VDT matters in different ways for different kinds of products. If you mainly work on an application, then you probably aren‘t as concerned with small stylistic shifts that can happen. It doesn‘t super matter that some button or form element is 4px left of where it was the last run. You probably are more interested in using VDT as a “smoke test” for a broken app. Such as to guard against your best friend Claude slipping in a &lt;code&gt;* { opacity: 0.6; }&lt;/code&gt; in your global stylesheet while you were dozing off at your computer waiting for your token-burning machine to be done “thinking”. Devs focused on sites and apps will mostly likely test screenshots of entire pages/flows just to confirm that nothing is wildly and obviously broken like the main “pay us money” CTA button being covered up by a rogue ad placement.&lt;/p&gt;

&lt;p&gt;For UI component library devs like me, particularly design system devs like me, VDT is much more involved. Design systems are very interconnected libraries by nature. Design system components get used all over the place in other components, so it is very easy to make a change to a foundational component that yields wildly unexpected results across the codebase in a component you didn‘t even look at. For library devs, running VDT is more of a ”break the build immediately, throw a wall of red text” kind of situation rather than a ”oh yeah, we should really fix that padding on the sign up page sometime" one.&lt;/p&gt;

&lt;p&gt;Also, we test components that have many more variants/states than the specific composition of an entire app. The humble button component has a theme, variant, rank, pill shape, with icons, loading state, and all the interactivity styles for &lt;code&gt;:hover&lt;/code&gt;, and &lt;code&gt;:focus-visible&lt;/code&gt; etc. To do VDT correctly, every single one of those visually distinct states should be tested so that we can have confidence we aren‘t introducing unwanted problems (tech debt) into our libs.&lt;/p&gt;

&lt;p&gt;The fact that VDT is such an integral part of UI library development is why its STUPID that its still so damn hard to wire up.&lt;/p&gt;

&lt;p&gt;&amp;lt;rant&amp;gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Here are the stupid hard things about VDT
&lt;/h2&gt;

&lt;p&gt;Having just spent WAY too long trying to hook up a VDT approach, even in the bursting-at-the-seams-with-AI world we live in (AI didn‘ help at all, btw. It utterly failed at a lot of the final approach), here are the things that I’ve run across lately that are bugging me about VDT being so hard to get right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Counting pixels sucks
&lt;/h3&gt;

&lt;p&gt;It seems like the best we can really do is to examine the color data for each pixel in two screenshots and then mark one of them pink and say “this one is different!”. If we do that over and over again, we will know all the tiny differences in each screenshot. But do you want to know how easy it is to absolutely destroy the pixel counting approach? Just add a box-shadow to your component with a blur or spread. The dithering approach in each browser can be enough to trigger changes being registered which might be enough to end up failing your build. Isn‘t that great?! We UI devs have no control over the way a browser draws its translucent gradients, but they can for sure destroy our pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thresholds are never good enough
&lt;/h3&gt;

&lt;p&gt;Part of initial problem of the “counting pixels for differences” approach is that we know in advance that not ALL of the pixels in the screenshot are ones we care about. We don’t really care that much about box-shadow dithering, or how the browser makes text bold. So most of the VDT tools out there allow us to set thresholds for ”how many differences are ok before we get mad and break your build”. Most of the thresholds are expressed in terms of percentages—i.e, how much of the screenshot can be different before angry errors happen. But this dumb threshold approach isn’t ever good enough, because box-shadow differences can add up quickly and still lead to a bunch of false positives with no real way to actually get your CI build to pass. So after struggling for hours, devs will inevitably just raise the thresholds so they can continue along with their lives, and that introduces more chance for actual visual regressions to happen without CI ever going red.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the screenshots in CI when things change on purpose is always hard
&lt;/h3&gt;

&lt;p&gt;Not every visual change is a problem. We update our UIs. We fix bugs. We make breaking changes to innovate. Any time we make purposeful visual changes, the baseline images need to be updated. We have to mark somehow that some of the baseline images are ok to change, possible alongside others that are not ok to change. How do you mark purposeful changes if you are trying to automate VDT? CI builds can‘t really accept user input, and asking the user ”Did you mean to change these buttons to green?” destroys the whole concept of automation anyway. Deciding when &amp;amp; how to purposefully update the baseline images is complicated because its essentially cache-busting which is one of the hardest problems in computing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Image storage is a huge pain
&lt;/h3&gt;

&lt;p&gt;Admittedly, this pain point might just be because I‘m a design system dev, but we have a LOT of images. Our button component alone has 500 different screenshots. And that is including the main “kitchen sink“ page that renders all the default states and variants together in a single screen shot. Our design system buttons have &lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus-visible&lt;/code&gt;, and &lt;code&gt;:active&lt;/code&gt;, so our VDT script has to test each of those individually.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If there is a way to hover over more than one element at a time in an automated test, please let me know &lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;over on Bluesky&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So our VDT script has to generate a screenshot for each of those interactive states, for each of our 4 different kinds of buttons. Our system also has 3 themes, and light &amp;amp; dark modes. So thats nearing 20 images for each different stylistic variant of buttons.&lt;/p&gt;

&lt;p&gt;Deciding where to keep all those images is a headache. For VDT to work, we need a “baseline” screenshot and a “current” screenshot. While running your VDT test, thats 1.000 images or so JUST for buttons. Multiply that same concept for the other components with lots of stylistic variants like badges and alerts and the pile of images stacks up real quick.&lt;/p&gt;

&lt;h4&gt;
  
  
  Storing them in the repo
&lt;/h4&gt;

&lt;p&gt;The simplest option might be to just store the baseline screens in your repo alongside your code and check them in. That totally works, but GitHub was not designed for binary file storage, it was designed for text files. GitHub has only recently implemented their “large file storage” feature so that you don’t have to clone down those 1.000 binaries and/or have them stored in each version of the history of your repo. But if you have a ton, then you still have to work around how GitHub‘s LFS storage works. I haven‘t tried that yet, because I opted for what a lot of folks do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Store them in an external place or tool
&lt;/h4&gt;

&lt;p&gt;Because the repo is a cumbersome place to store images, devs start looking for other places more suitable, AWS S3 buckets and such that are more designed to handle large batches of file types beyond just text. Some devs even opt to use an off-the-shelf tool that just does all the image storage and the pixel counting in a single place for you, while charging you an enterprise license fee and undoubtedly having a borderline unusable dashboard UI for viewing VDT test results. If you store your VDT screens in an external bucket like S3, that‘s less complex, but you still have to manage getting the images to &amp;amp; from your build in order to do the pixel matching.&lt;/p&gt;

&lt;p&gt;If you roll your own pixel matching using some library, then your build pipeline has to have a step in it now called “Go get all the baseline images from S3”. That adds latency and performance cost to your build that will add up. Now your CI pipelines takes an extra few minutes over and above actually generating the screens and doing the pixel matching. Plus the complexity cost of adding build scripts to put the screens where the pixel match test tool thing expects them to be. Ever written a script to run through a pile of images and put them into sub directory folder structures according to splitting the file name so you can put the button screenshots in the &lt;code&gt;/button/tests/__screensots__&lt;/code&gt; folder so that the test runner knows where to look? Giant pain in the ass.&lt;/p&gt;

&lt;h3&gt;
  
  
  External tools can take results out of your pipeline
&lt;/h3&gt;

&lt;p&gt;So you don‘t want to do all that roll your own and opt for an external tool. Your boss says the license is worth it and the dashboard UI isn‘t really that bad, it‘ll just take some training and getting used to. You'll still have to write a script that integrates with that tool, or you‘ll have to design your pipeline such that all the VDT tests run directly on the tool’s platform (looking at you Saucelabs). Either way, viewing the results mostly likely happens in the dashboard of that tool and NOT in your pipeline. Sure, you do the best you can, but your CI probably ends up a lot like mine where it will fail and error telling you that some screenshot did a bad, but you’ll have to actually login to that tool’s UI to actually see what about that particular screenshot failed.&lt;/p&gt;

&lt;h4&gt;
  
  
  External tools require more management
&lt;/h4&gt;

&lt;p&gt;External tools mean more logins, more management of team access, and more heartache. If you rely on an external tool for results, then everyone on your team needs to be able to get in to see them and also to run their own tests on the PR branches. The tool I use (not going to name and shame cuz its likely not their fault) requires access tokens to send images up to their environment to be tested. That means that every team member needs to have their own token, because having just a single token for the whole team to share is a security no-no. So guess who gets to manage the team‘s access to the tool? You &amp;amp; me. Good luck getting Claude‘s help trying to figure out how to generate a new token for a new team member when you only go to that screen once a year. :)&lt;/p&gt;

&lt;h3&gt;
  
  
  VDT UIs are necessarily messy
&lt;/h3&gt;

&lt;p&gt;This gripe is just me yelling at clouds I suppose. I don‘t really think there is a whole lot that VDT tool companies can do about their complicated UIs. I suppose that VDT UIs are just inherently complicated because of how complex VDT itself is. But that said, every UI I‘ve ever seen in a VDT testing external tool has been super busy and full of options I never seem to know how to use. There are buttons for turning the diff highlights on and off again, zooming the images, flipping back and forth from the baseline image to the current one, accepting desired purposeful changes to the baselines, and on and on.&lt;/p&gt;

&lt;p&gt;And even if all of those features were wiped away, you‘re still left scrolling through potentially thousands of similar-looking screenshots trying to find the one marked red. Better hope the filtering options in the external tool are on point. More fun still, if the tool allows you to batch up your tests into groups, thats great, but guess who writes the integration code that decides on the group names? You &amp;amp; me. I do it by file name. When I generate the screenshot, I make the file name have a predictable format that includes the browser, theme, mode, and some property &amp;amp; value combo being tested. Then when I go to upload my screens to the external tool, I get to parse that filename back out again and group all those screens into batches that are basically tied to whatever the external tool UI is made to show. Figuring out how to make sense of thousands of images so you can filter and sort them later on in a UI you don‘t control is painful.&lt;/p&gt;

&lt;h3&gt;
  
  
  There are too many unpredictable timing variables
&lt;/h3&gt;

&lt;p&gt;Since the main purpose of a VDT test is to render a screenshot in an automated pipeline environment, a lot of care must be taken to ensure that each visual scenario being tested is actually the same scenario over and over again. There are too many things about building UIs on the web that make this consistency hard. Does your component load a custom web font? Then your screenshot needs to wait until that font is loaded &lt;strong&gt;and visually applied&lt;/strong&gt; before taking the screenshot otherwise your VDT tests will be full of false positives. The font will eventually load and your beautiful typography is fine, but if the screenshot gets taken too fast, you‘ll bust your thresholds. We can detect when a font is loaded, but there‘s no way to wait until that re-paint has been completed as far as I know. So we just guess and hope its done?&lt;/p&gt;

&lt;p&gt;I am a web component developer, so I also have to wait until each of my web components is defined and has run its first update cycle before taking a screenshot. I use &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; which definitely helps a ton. Lit has a handy &lt;code&gt;updateComplete&lt;/code&gt; property that is a &lt;code&gt;Promise&amp;lt;boolean&amp;gt;&lt;/code&gt; that resolves &lt;code&gt;true&lt;/code&gt; each time the component render cycle is completed. But what if there are child web components in the shadow root? Waiting until every component has completed its render cycle is another way that false positives can plague us. There is just no simple way to know for sure, so we &lt;code&gt;setTimeout&lt;/code&gt; for some random number and hope its long enough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing animating elements is impossible
&lt;/h4&gt;

&lt;p&gt;What about trying to visually test the loading state of your button with its delightful spinner spinning away. Can‘t test that in a screenshot! There is no way to guarantee (that I know of) that the screenshot will be taken at the exact same time relative to when that loading spinner started spinning, so you either get the flakiest test known to man, or you‘re like me and just say “I guess we can’t test animated things with VDT at all”. Then you chuck those loading state VDT tests out the window and just hope you never break them accidentally.&lt;/p&gt;

&lt;p&gt;But thats not all!&lt;/p&gt;

&lt;h4&gt;
  
  
  Screenshot size management
&lt;/h4&gt;

&lt;p&gt;Since VDT takes a screenshot of all or a portion of the page, care must also be taken that the images are the right size. If you want to take a screenshot of a single button, but you code &lt;code&gt;page.screenshot()&lt;/code&gt; with a 2000px viewport, you won‘t even be able to see the button. Therefore, to make the screens both usable for automated testing and visually scannable by a human (sorry AI, you still aren‘t good enough at images yet to help here much either) then you will find yourself having to design some test UI page that carefully crafts predictable container elements that you can screenshot into predictable sizes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember that if the size of a screenshot changes from the size of the baseline image, thats a failed test! Even if the contents of the image didn‘t change because pixel diffing is dumb.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And each visual testing environment has its own default viewport. I use &lt;a href="https://vitest.dev/guide/browser.html" rel="noopener noreferrer"&gt;vitest - Browser mode&lt;/a&gt; which spins up &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; under the hood, and I have no idea what the default viewport size is when a headless Chromium instance spins up. Is it the last viewport size that it was set to the last time it was run, or some static default? I have also seen that the size of the screenshot that you take with the &lt;code&gt;screenshot()&lt;/code&gt; function depends on what size the viewport is. Your components also depend on what size the viewport is and it needs to be the same every time or you‘ll get a pile of false positive failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  You basically have to code a mini app
&lt;/h3&gt;

&lt;p&gt;If you are testing components like me, they probably don‘t exist entirely in isolation. If that button is themeable, then you need to test it IN the themes. So you need to spin up a test page with that theme‘s global css on it, but none of the others because your tests need to run in isolation. Your components also work in dark mode? Then you need to set that up also. Oh yeah, that‘ll add another 500 images now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Kitchen sink tests don‘t help a ton
&lt;/h4&gt;

&lt;p&gt;You try to avoid billions of images so you create ”kitchen sink” screenshot tests where you render lots of different variants of the same component type on the same page at the same time. That‘s a fun script to write. Nested &lt;code&gt;for...in&lt;/code&gt; loops for days! Also remember that &lt;strong&gt;not every property and value your components have actually have a visual component to them&lt;/strong&gt;. That &lt;code&gt;label=&lt;/code&gt; property that just sets what the text is doesn‘t matter, so you‘ll also have to manually make an array of the props that DO have a visual component to them and only render/test those to help keep the screens down to manageable numbers (not possible).&lt;/p&gt;

&lt;p&gt;And once you make your kitchen sink screenshot, the error message you get when something is wrong is the opposite of helpful. Because you grouped your components together in an attempt to cut down on bandwidth and storage sizes, the error you‘ll get when you bust a threshold will be something like ”One or more of these components, or something about the mini-app UI around them broke something. You get to go visually look at the kitchen sink image to figure out what it was now.” So your CI pipeline will just break and you need to pull up the actual screenshot to figure out which variant(s) it is that are the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we do this to ourselves?
&lt;/h2&gt;

&lt;p&gt;Is the screenshot method truly the only way we really have of verifying visual issues? To me it seems like a huge disconnect that the only approach we seem to have come up with as a community to test the malleable, fluid world of stuff on the web is to create an absolutely rigid screenshot approach that absolutely cannot change in any way (notwithstanding the purposeful updates) or else the whole system breaks. Does that seem wrong? Why is this super rigid VDT testing format the accepted approach?&lt;/p&gt;

&lt;p&gt;If this is the only way to do VDT, then VDT is just one of those sucky things about building stuff on the web, like naming CSS selectors and responding to web component haters on Twitter. If the screenshot method isn‘t the only way, where are the other ways? Is anyone out there working on a better way to visually statically analyze our components and apps so that we don‘t have to do this stuff? Can we come up with a mechanism that isn‘t so prone to timing issues, race conditions and brittle scenario building? I sure hope so. I‘m tired.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>Why isn‘t anyone talking about model collapse?</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 01 Mar 2026 05:01:46 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/why-isnt-anyone-talking-about-model-collapse-208f</link>
      <guid>https://dev.to/michaelwarren1106/why-isnt-anyone-talking-about-model-collapse-208f</guid>
      <description>&lt;h2&gt;
  
  
  This is the last topic I want to write about
&lt;/h2&gt;

&lt;p&gt;And I‘m fairly sure that you all out there are also starting to get sick of all the talk about AI on the interwebz these days. The tech world has seen its biggest content creators just go wild about AI. Matt Pocock, "the Typescript" guy hasn‘t posted about Typescript in &lt;a href="https://www.youtube.com/@mattpocockuk/videos?view=0&amp;amp;sort=dd&amp;amp;shelf_id=1" rel="noopener noreferrer"&gt;8 months or so&lt;/a&gt; (and his comment sections let him know it on every new AI video). Half of my &lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;Bluesky feed&lt;/a&gt; is filled with AI talk, and lets not mention LinkedIn shall we?&lt;/p&gt;

&lt;p&gt;But in all the discourse, paid shilling, hype-mongering, and anti-AI sentiment alike, this one topic doesn‘t seem to come up nearly as much as I think it should and I have questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why aren‘t many people talking about model collapse?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is model collapse?
&lt;/h2&gt;

&lt;p&gt;Let me start by explaining the concept just in case it might be new to you. I‘ll use the &lt;a href="https://en.wikipedia.org/wiki/Model_collapse" rel="noopener noreferrer"&gt;Wikipedia definition&lt;/a&gt;, which defines the term as&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Model collapse&lt;/strong&gt;: a phenomenon noted in artificial intelligence studies, where machine learning models gradually degrade due to errors coming from uncurated synthetic data, or training on the outputs of another model, such as prior versions of itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Basically, the more these LLMs are trained on their own slop, the more worthless they become.&lt;/p&gt;

&lt;p&gt;I was sent &lt;a href="https://www.youtube.com/watch?v=ShusuVq32hc" rel="noopener noreferrer"&gt;this video by by George D. Montañez, PhD&lt;/a&gt; of Theos Theory on YouTube. Its one of the better talks about AI that I‘ve seen, though I admit I‘m a bit biased because of its content. In that very well formatted presentation, George highlights that by somewhere around the 9th generation of training an LLM on its own output, the output it can generate is basically gobbledegook.&lt;/p&gt;

&lt;p&gt;That same Wikipedia article I pulled the definition from also says that this concept of model collapse is widely known by the AI research community. Its not a secret that this kind of thing happens.&lt;/p&gt;

&lt;p&gt;In my mind, the biggest question that industries poised to consume these models at scale is: &lt;strong&gt;What specifically, if anything, is being done to prevent model collapse?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I went researching (the old fashioned way by typing my query into a Google search input) about what folks say about preventing model collapse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is model collapse even a problem?
&lt;/h3&gt;

&lt;p&gt;I found at least one &lt;a href="https://arxiv.org/abs/2404.01413" rel="noopener noreferrer"&gt;study from 2024&lt;/a&gt; that seems to show that as long as the AI output doesn‘t replace the human training data, then everything will be ok. So there‘s nothing to worry about right? I guess that depends on what we think will happen to the whole ocean of digital content in the near future. Sure, some parts of it won‘t get replaced by AI output. The &lt;a href="https://arxiv.org/html/2601.02671v1" rel="noopener noreferrer"&gt;full text of Harry Potter and the Sorcerer‘s Stone&lt;/a&gt; which is already in the models practically word for word won‘t change because (hopefully) none of the trillion AI rewrites of it will get published. Or will they? I think there‘s a really good chance that Temu Harry Potter Stories will start popping up all over the internet. Our society has already fallen in love with fanfics, so it would not surprise me one bit to see new AI output chapters exist alongside the real thing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Aside—This hopefully goes without saying, but fuck J.K. Rowling even if she did create a pretty cool universe absolutely fucking filled to the brim with plot holes and mildly racist character names.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Take Harry Potter AI rewrites for the lulz, and multiply it by a billion and I think we could agree that its not terribly far-fetched that even if AI content doesn‘t “replace” the human-made originals, the presence of that generated content alongside the original certainly could muddy the waters quite a bit. What if the AI generated content gets picked up as training data because its posted on some blog, but the original doesn‘t because the publishing company sues the AI bros and disallows it? Then what?&lt;/p&gt;

&lt;p&gt;But we‘re coders you and me, right? Code is our domain. So in addition to the potential difficulty with the loads of training data written in natural languages, we also have to concern ourselves with the question of how likely do we think it is that AI generated code replaces or outpaces human code in the future? And if that does happen, how long does it take, how much AI code would it take to poison a model beyond repair? No one knows the future, but here‘s my leading question about all of this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Isn‘t the proliferation of AI generated output what these billionaire AI tycoons want? Isn‘t that their whole goal and business model?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  In a world where AI output proliferates massively, then what?
&lt;/h2&gt;

&lt;p&gt;I think we could all agree that the AI business bros are pushing heavily to make sure that every human adopts their tools. That‘s how they make money. So do we really think that OpenAI and Anthropic and the like survive purely on their chatbot apps that only produce content to be read by their human users, but never to be published back out into the environment? I doubt it. I don‘t see adoption of these models at the scale that seems to be needed. The muggles (non-tech folk) don‘t really seem to be adopting AI en masse in their daily lives. Sure they might use it, but paying for it? Doubt. I‘ve also red that there is a fair bit of negative sentiment about AI in general because of the explosion of useless, pointless chat bots in every single app and website ever made.&lt;/p&gt;

&lt;p&gt;I had ChatGPT installed on my personal phone for a while, but I never gave them a dime of my personal money. I‘m a techie and I still rarely typed at the word prediction machine very often. I have since deleted the app because of the &lt;a href="https://www.cnn.com/2026/02/27/tech/openai-pentagon-deal-ai-systems" rel="noopener noreferrer"&gt;shit they pulled in making a deal with this current dumpster fire of an administration&lt;/a&gt;. But I won‘t miss it at all. Not even a little. I have used an AI chat bot in my personal life maybe 10 times in all. And I would bet that even though OpenAI has a ton of users, they aren‘t going to make a ton of money unless companies are paying en masse to create things. Creating things means putting that content where? Back out there into the space where OpenAI and Anthropic pulled their training data from.&lt;/p&gt;

&lt;p&gt;So if we agree that these companies NEED for folks to actually create things using their tools in order for them to make the money they want, then the amount of AI generated content will explode as they achieve their goals.&lt;/p&gt;

&lt;p&gt;What then? What happens when Anthropic gets their wish and LOTS and LOTS of things are created and put back out into the universe with their tool? Doesn‘t that eventuality virtually guarantee model collapse?&lt;/p&gt;

&lt;h2&gt;
  
  
  How do the business models of these AI companies align with the concept of model collapse?
&lt;/h2&gt;

&lt;p&gt;How can two opposite things be true simultaneously? How can AI content proliferate so massively that these companies are able to achieve the necessary ROI for their investors without causing model collapse at the same time? OpenAI is said to be adding ads to their platform, but that won‘t get the ROI they need either.&lt;/p&gt;

&lt;p&gt;Are these CEOs knowingly driving all of us towards a future where their products inevitably fail? How can they make money and it not also be true that so much AI content is generated that the models collapse? We devs know that 9 training generations isn‘t that many minor version bumps. I‘ve seen precious few questions about this topic, and the answer the CEOs seem to always give is "We will have to be very careful about what we put into our training data set." As if that explains everything. I still have questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, so you can limit the training set to exclude enough AI content. How?
&lt;/h2&gt;

&lt;p&gt;So let‘s assume that just not including AI content in the training set is the answer to model collapse. Cools. What does that look like exactly? How would these models expect to improve and get better? If more AI content is being produced, but these companies cant use any of it, doesn‘t that mean they depend on more human generated content for their models to improve? Am I the only one seeing a catch 22 here? These companies are driving us to a world where there will necessarily be less of the thing their product depends on to improve? What?&lt;/p&gt;

&lt;p&gt;And if excluding AI content prevents model collapse, how exactly could that be done? I know that us humans can all tell when something was written by a bot, but how can that distinction be made programmatically at the scale needed by these models? Its not like every website is going to have some comment in the code saying &lt;code&gt;// bot code, please ignore&lt;/code&gt;. How exactly would AI content be excluded? I have not seen an interview with any of these CEOs where they explain a process they would have to identify and segregate AI content from human content. I don‘t know anything about AI training, but I am a dev, so I would tend to think that task to be largely impossible.&lt;/p&gt;

&lt;p&gt;Why is no one pressing these CEOs about model collapse? Why is no one asking questions about what steps these companies are already taking to mitigate it? As the hype train tramples us underneath, I think its dragging us to a world where these models get worse not better. I would love to see someone ask an AI component CEO why that isn‘t case just to see what they say.&lt;/p&gt;

&lt;h2&gt;
  
  
  I think they know what they are doing
&lt;/h2&gt;

&lt;p&gt;I am eternally a cynic. Especially in this cases like this where the hype of thing is SO overblown compared what I‘ve actually seen in real life. I know its cliche at this point, but I had the same reaction to NFTs. Here‘s me hoping that these LLMs go the same route. And I think they will. And I think the CEOs know it. My personal opinion is that this might be one of the biggest pump and dump schemes of all time. I have a sinking feeling that the purveyors of these models know full well that model collapse is inevitable if they achieve their goals, so they just want to get in, pump their stocks and valuations to record highs, sell out a bigger fish and then leave the FAANG companies holding the massive bag of negative sentiment once it all comes crashing down. I don‘t believe any of them for a single second when they say that all of the things they are doing are "about humanity" or "the greater good" or whatever line it is they are hoping we buy today. I don‘t believe these LLMs are capable of any of the things that the hype train says they are. Go watch that presentation from George. These models don‘t reason, and it doesn‘t matter that Anthropic calls it a "reasoning model". Its a trick. And its on purpose. And its going to cost our society a shit ton of money and who knows what else along the way.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Can we please stop applying human traits to chat bots?</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Wed, 25 Feb 2026 03:32:02 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/can-we-please-stop-applying-human-traits-to-chat-bots-44if</link>
      <guid>https://dev.to/michaelwarren1106/can-we-please-stop-applying-human-traits-to-chat-bots-44if</guid>
      <description>&lt;h2&gt;
  
  
  Let‘s start the new year off with a rant shall we?
&lt;/h2&gt;

&lt;p&gt;Tech world we need to talk. Not you normal people just out at the grocery store, you all couldn‘t be bothered all that much with AI or LLMs, so I‘ll just talk to the tech folks this time. I have been watching and listening to videos and podcasts all year, in varying levels of hype about AI and these super cool but also highly probably crappy LLMs and all their catchy new features. Every host of these shows has a slightly different take on AI, but one thing that comes up a lot that drives me up the wall is when folks &lt;strong&gt;anthropomorphize&lt;/strong&gt; LLMs. So this is a petition to cut it out.&lt;/p&gt;

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

&lt;p&gt;I‘ve typed it twice and I‘m exhausted. Thank goodness for spell check VS Code plugins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Anthropomorphize&lt;/strong&gt;: verb;&lt;br&gt;&lt;br&gt;
attribute human characteristics or behavior to (a god, animal, or object).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“Anthropomorphize” is a long word that means to assign human characteristics, behaviors, or traits to an object that necessarily isn‘t human. And we humans do it all the time to everything. Did you know that ships “have a gender”? (They‘re women). Cars are mostly women too. Its very common to give a car a human name. So common, that there are movies about it. Remember &lt;a href="https://en.wikipedia.org/wiki/Herbie" rel="noopener noreferrer"&gt;Herbie&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Christine_(1983_film)" rel="noopener noreferrer"&gt;Christine&lt;/a&gt;? Those cars tell how old I am, but had human names and were sentient with human emotions like jealousy and anger.&lt;/p&gt;

&lt;p&gt;I‘m neither an anthropologist nor a neurologist, so I haven‘t got foggiest idea why we anthropomorphize non-human objects, but we do it a lot. We say that really smart birds can “talk” to us. (accidental analogy to LLMs). When the technological marvels in our world don‘t do what we expect, we say things like "It must be grumpy today". There are a million examples.&lt;/p&gt;

&lt;p&gt;In general, I would say that we anthropomorphize—I‘m going to set a record for the number of times that word appears in an article—things that we care about and to which we have attachments. Not always, but I would guess that the closer the object is to us in our daily lives, the more likely we are to assign it human qualities.&lt;/p&gt;

&lt;p&gt;Therefore, I feel the need to implore you to&lt;/p&gt;

&lt;h2&gt;
  
  
  Please fight the urge to anthropomorphize LLMs and chat bots
&lt;/h2&gt;

&lt;p&gt;I know this will be easier said than done, that perhaps the ship has already sailed, but I think its important and it drives me crazy when I hear LLMs assigned human qualities out in the wilds of the techiverse.&lt;/p&gt;

&lt;p&gt;I‘m not going to name names or call anyone out specifically, but here are a few examples I‘ve heard or seen recently that drove me crazy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heard a podcast personality refer to an LLM as a "he"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I cannot even begin to describe how weird it is to me to assign an LLM a gender. We have enough problems with description, reference and assignment of gender for humans! (Don‘t get it twisted, trans rights are human rights) The last thing we need is to start assigning gender to LLMs, especially if the default is "he". As an unwitting member of the patriarchy....eww.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heard a podcast personality say they "had a conversation with Claude"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, podcast person, you didn‘t. You did not have a conversation with the statistical word predictor machine. A conversation is between two human beings. You cannot have a conversation with a pile of binary. You typed your words into an input box, and a pile of math sent you a formatted API response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anytime anyone says an LLM is “thinking”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I cannot tell you how many times I‘ve heard human beings say things like “I prompted it, and it thought for a bit, came up and an answer that it thought was correct”. LLMs do not think! There is no actual thinking going on. I hope that all of us knows that LLMs are calculating which word is the most likely to come next and printing that to the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Its important that we don‘t think of these things as human-like
&lt;/h2&gt;

&lt;p&gt;I know that its going to be difficult to come up with words that describe these prediction machines without using human-like terminology. The output they create is human-like enough to trick our brains just like the squawking sounds of birds that “can talk”. Birds aren‘t ever talking. The ones that do it the best are smart enough to know that mimicking our sounds gets them attention and treats. LLMs aren‘t smart. They cannot be, they don‘t have brains. Thankfully, they can only do what they have been programmed to do, even if that program is open-ended and non-deterministic in its results.&lt;/p&gt;

&lt;p&gt;But I think its important that we try not to anthropomorphize these objects. I think that if we go too far in that direction we will lose our ability to discern the quality of their output. There have already been discussions online about who is “nice“ to their LLMs and whether or not we should be polite when typing into form fields on the UIs that front them. If we anthropomorphize them too much, who is to say that we won‘t start being hesitant to correct these models out of that same desire to be polite? It might be a bit of a stretch, but I compare this to like when couples start not feeling comfortable kissing in front of the family dog because they assigned some human feeling or emotion to the dog that couldn‘t give a flying rats ass and has no idea what is even going on.&lt;/p&gt;

&lt;p&gt;We should never hesitate to keep these models in their place, as tools (of varying usefulness) in a tool belt. When my hammer stops doing its job, I won‘t console it or try to teach it, I‘ll just use a different hammer. The more we assign human qualities to these objects, the more objectivity we lose in our judgement of their usefulness. We will start giving these models that provide atrocious output “points for trying” to our detriment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model and bot creators stack the deck against us
&lt;/h2&gt;

&lt;p&gt;The builders that make these chat bots do this on purpose. They KNOW that giving their prediction state machine a human-like name—looking at you Claude, Devin, Watson, Siri, Alexa (credit due to ChatGPT for NOT doing that surprisingly, honorable mentions to Grok and Gemini)—will entrench that attachment in our brains. They know more of the psychology of anthropomorphism than I do. It reminds me of the quote from &lt;a href="https://en.wikipedia.org/wiki/I,_Robot_(film)" rel="noopener noreferrer"&gt;I, Robot&lt;/a&gt; where Will Smith's character says to the robot doctor scientist lady "Why do you give them faces? Try to friendly them all up, make ‘em look human?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is 100% by design.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The output these LLMs create refer to themselves with human pronouns out of that same design and a smidge of necessity. The builders want us to get attached to their product so we‘ll give them all our money. But the LLMs have been trained on human speech, and only humans have any concept of “self” and therefore only humans need a way to refer to themselves. So these models were likely predestined to refer to themselves using human language, since that is the only language they could possibly have adopted for their output.&lt;/p&gt;

&lt;h2&gt;
  
  
  End rant
&lt;/h2&gt;

&lt;p&gt;I know its hard, but can we please try to stick to pronouns like “it” to refer to these things? Can we stop pretending that these bots are human-like? In researching the names for popular chat bots, I stumbled on a Reddit thread where folks were having their chat bots name themselves. Could we also stop doing that please? It will be tricky, and the responses these tools give us will trick our brains, so we‘re fighting our own internal pattern recognition machines inside our skulls (that are like way, way, way, way, way, way better than the bots and a trillion percent more convincing to us as well) every time we read the output from a bot.&lt;/p&gt;

&lt;p&gt;But if we stay on this same path, guided by the folks trying to make trillions of dollars off our ever-increasing context usage, we‘ll start losing our objectivity about them.&lt;/p&gt;

&lt;p&gt;So remember kids, its a prediction machine not a person.&lt;/p&gt;

&lt;p&gt;/rant&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Inheritance: a web component super power</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Wed, 25 Feb 2026 03:30:06 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/inheritance-a-web-component-super-power-34pm</link>
      <guid>https://dev.to/michaelwarren1106/inheritance-a-web-component-super-power-34pm</guid>
      <description>&lt;p&gt;By now, you have realized that I basically only post discussions or implementations that have come up on the job. This one is no different. I want to walk you through an implementation that we did recently and highlight a super power of web components that can be very useful when used correctly.&lt;/p&gt;

&lt;p&gt;I‘ll use a few examples that show exactly how I use inheritance to my benefit when building systems of components, and I‘ll also give a word of caution, because great power also comes with great responsibility&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%2Fkkt98ssg5peyxzy4xqhc.gif" 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%2Fkkt98ssg5peyxzy4xqhc.gif" alt="Gif of Mr Burns looking at photos of his parents and saying " width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So we‘re full on OOP now?
&lt;/h2&gt;

&lt;p&gt;I know that seeing the word “inheritance” from the jump and knowing that I‘m a web developer that loves making UI components probably has you nervous. You‘re probably worried that this article is about to take a turn down the dreaded Object-oriented Programming (OOP) paradigm path. Am I going to recommend that web apps be completely built with classes and that the Java devs were right all along and OOP is the way? Definitely not. Rest assured, components are basically the only place where I think sprinkling a little bit of OOP principles into our libraries can make a big difference. I am not going to tell you to go full bore OOP. That would just be silly. We‘re all JS devs around here, we write scripts. But using classes can help us get organized some and thats a good thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is inheritance?
&lt;/h2&gt;

&lt;p&gt;If you‘re anything like me you probably need a little bit of a primer on what "inheritance" actually is from an object-oriented programming perspective. So lets dig into what inheritance actually is a little bit before I talk about how we can use it.&lt;/p&gt;

&lt;p&gt;Inheritance in Javascript is basically the idea that a class extends another class. We can call the two classes in this inheritance model the “parent” class and the “child” class. A simple example of inheritance might look something like this:&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;Animal&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;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="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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;animal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numberOfLegs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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;name&lt;/span&gt; &lt;span class="o"&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="nf"&gt;introduce&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`My name is &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;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. I am of type: &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="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Animal&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;name&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="nx"&gt;name&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="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dog&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animal&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;Animal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Aragog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;introduce&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// My name is Aragog. I am of type: animal&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dog&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;Dog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fluffy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;introduce&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// My name is Fluffy. I am of type: dog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the parent class is &lt;code&gt;Animal&lt;/code&gt; and the child is &lt;code&gt;Dog&lt;/code&gt;. When we use the &lt;code&gt;extends&lt;/code&gt; keyword in JS, then we establish an “is a” relationship between the parent and child classes. So it can be said that because &lt;code&gt;class Dog extends Animal&lt;/code&gt;, then a &lt;code&gt;Dog&lt;/code&gt; is an &lt;code&gt;Animal&lt;/code&gt;. The &lt;code&gt;Dog&lt;/code&gt; class will “inherit” all of the properties and methods of the &lt;code&gt;Animal&lt;/code&gt; class, but can overwrite those properties and methods if need be.&lt;/p&gt;

&lt;p&gt;Notice that we didn‘t have to define the &lt;code&gt;introduce()&lt;/code&gt; method in the &lt;code&gt;Dog&lt;/code&gt; class. That method is available on the instance of &lt;code&gt;Dog&lt;/code&gt; we created with the &lt;code&gt;new Dog('Fluffy)&lt;/code&gt; call but is only defined on the &lt;code&gt;Animal&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;That‘s inheritance. There is more to it, but that‘s all we need to know in order to talk about how we can use inheritance to our benefit when writing web components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design systems have natural “is a” relationships
&lt;/h2&gt;

&lt;p&gt;We‘ve talked before about how the fact that web components are backed by classes is the perfect system to represent having a single definition of some piece of UI and logic and then get to create as many instances of that definition as we want. Classes make a ton of sense when we‘re talking about components. And if we go further and start to make collections of components, like a design system, then some patterns start to emerge where inheritance can be super useful.&lt;/p&gt;

&lt;p&gt;Think about form fields? How many types of form elements are there in a typical design system? There‘s probably a generic &lt;code&gt;&amp;lt;text-field&amp;gt;&lt;/code&gt;, and then as your system matures you might make these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;password-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;number-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;credit-card-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;masked-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;currency-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&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%2Frg5z196ezbj1yj90rlfo.gif" 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%2Frg5z196ezbj1yj90rlfo.gif" alt="Gif of a man in a monkey suit saying " width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So a number of different types of inputs emerge, and a great question would be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aren‘t all of these types just text fields with more features?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yes! There are many different kinds of input fields that are basically text fields with more features&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A password field is basically the same as a &lt;code&gt;&amp;lt;text-field&amp;gt;&lt;/code&gt; except that its &lt;code&gt;type&lt;/code&gt; property is always &lt;code&gt;password&lt;/code&gt; (it might flip between them with some hide/show button feature). A number field is also a &lt;code&gt;text-field&lt;/code&gt; in nature but its &lt;code&gt;type&lt;/code&gt; is &lt;code&gt;number&lt;/code&gt; and there may be a masking feature to allow commas. There's actually a secondary “is a” relationship with &lt;code&gt;&amp;lt;masked-field&amp;gt;&lt;/code&gt; and the other fields that may be masked. A credit card field might only allow typing numbers in the field and may add spaces according to the detected card type. That‘s just a particular use case of the &lt;code&gt;&amp;lt;masked-field&amp;gt;&lt;/code&gt; which is itself still a &lt;code&gt;&amp;lt;text-field&amp;gt;&lt;/code&gt; in terms of all the other feature like validation, error state, helper-text options, slotted icons etc.&lt;/p&gt;

&lt;p&gt;So right off the bat, we have some pretty good use cases for inheritance with form fields.&lt;/p&gt;

&lt;p&gt;So if we were to organize these text field components into a hierarchy of features it might look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;text-field&amp;gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;number-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;password-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;masked-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;credit-card-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;currency-field&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;All the fields would inherit from &lt;code&gt;text-field&lt;/code&gt;. &lt;code&gt;number-field&lt;/code&gt; and &lt;code&gt;password-field&lt;/code&gt; would inherit directly. The &lt;code&gt;masked-field&lt;/code&gt; would inherit from &lt;code&gt;text-field&lt;/code&gt; and add a generic masking capability, such as the ability to specify a format via a &lt;code&gt;mask&lt;/code&gt; property. Something like &lt;code&gt;mask="00000"&lt;/code&gt; would only allow entering exactly five numeric characters (anyone else see a possible &lt;code&gt;&amp;lt;zipcode-field&amp;gt;&lt;/code&gt;?). Then &lt;code&gt;credit-card-field&lt;/code&gt; and &lt;code&gt;currency-field&lt;/code&gt; would inherit from &lt;code&gt;masked-field&lt;/code&gt; and specify a particular mask format. Maybe the currency field would always set &lt;code&gt;mask="$(0*)"&lt;/code&gt; and the &lt;code&gt;credit-card-field&lt;/code&gt; would dynamically change its mask depending on the card type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with inheritance
&lt;/h2&gt;

&lt;p&gt;So we‘ve decided that all of these form fields we want to make in our design system have a hierarchical relationship. Let‘s see how we would actually code this.&lt;/p&gt;

&lt;p&gt;Here's our base &lt;code&gt;text-field&lt;/code&gt; class&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 TextField class in Lit for simplicity&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitELement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// validation&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="nx"&gt;required&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;minlength&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;maxlength&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;min&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;max&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="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="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// a11y features&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="nx"&gt;label&lt;/span&gt;&lt;span class="o"&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="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;helper&lt;/span&gt;&lt;span class="o"&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// render the native input, native label tag, and helper-text elements&lt;/span&gt;
    &lt;span class="c1"&gt;// render any slots and basic structure&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;Then lets make our &lt;code&gt;number-field&lt;/code&gt; class&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;NumberField&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;willUpdate&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="kd"&gt;type&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="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;Yep. That‘s it. The &lt;code&gt;willUpdate&lt;/code&gt; is a &lt;a href="https://lit.dev/docs/components/lifecycle/#willupdate" rel="noopener noreferrer"&gt;Lit lifecycle hook&lt;/a&gt; that will be called before every update when any reactive property is updated. We can use this hook to force that the &lt;code&gt;type&lt;/code&gt; property that &lt;code&gt;NumberField&lt;/code&gt; inherits from &lt;code&gt;TextField&lt;/code&gt; cannot be set to anything other than &lt;code&gt;'number'&lt;/code&gt;. Every time the component updates, the &lt;code&gt;type&lt;/code&gt; property is set to &lt;code&gt;'number'&lt;/code&gt; regardless of what the consuming dev tried to mistakenly set it to. If we‘re not adding any other new features like masking, which according to our inheritance diagram we‘re not. Then &lt;code&gt;number-field&lt;/code&gt; is done! When we defined it as &lt;code&gt;&amp;lt;number-field&amp;gt;&lt;/code&gt; and use it, a &lt;code&gt;&amp;lt;number-field&amp;gt;&lt;/code&gt; will have all the same slots, validation logic and a11y considerations from &lt;code&gt;text-field&lt;/code&gt; but we haven‘t had to repeat ourselves. Inheritance takes care of everything for us!&lt;/p&gt;

&lt;p&gt;Now let‘s do &lt;code&gt;password-field&lt;/code&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PasswordField&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;willUpdate&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="kd"&gt;type&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="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;Finished!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;masked-field&lt;/code&gt; is a little more complicated because we are adding a new feature. So that class might look something like this:&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;MaskedField&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TextField&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// add a new property on top of the existing props from TextField&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;property&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mask-format&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;maskFormat&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="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;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectedCallBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// initialize masking library&lt;/span&gt;
    &lt;span class="nx"&gt;maskingLibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initialize&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;maskFormat&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;Once we have our generic &lt;code&gt;masked-field&lt;/code&gt; class that can accept any type of mask format and initializes the masking library we‘ve chosen, we can use inheritance again to make more fields that use a specific mask format. Let‘s make the &lt;code&gt;currency-field&lt;/code&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MaskedField&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TextField&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="nx"&gt;maskFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$(0*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// call the connectedCallBack of the `masked-field` class after setting the mask format&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectedCallBack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// always set the maskFormat property to the desired mask&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;maskFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$(0*)&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;
  
  
  Inheritance can help keep your code DRY, but use caution
&lt;/h2&gt;

&lt;p&gt;All those classes are pseudo-code. The real versions of them are more complex than the blueprint I‘ve shown here. But I hope that you can see that intelligent usage of inheritance can really help prevent duplication of shared features. The inheritance pattern works really well in the situation above because we want to share almost all of the features from the parent class. Inheritance is best used when there is a very strong “is a” relationship between the parent and child and the child class will not be overriding or resetting very many of the features from the parent class. If you find yourself changing or overriding too many features from the parent, then definitely consider breaking the inheritance chain and just making two separate classes and sharing the features that need it some other way like by importing a library of shared helper functions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Inheritance patterns are only recommended for internal use in a system. Consumers of your components with inheritance should use caution when extending components “from the outside”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Design system authors can use inheritance sparingly where there is a strong “is a” relationship and not have very many issues. The situation is a bit different for external consumers of that same system, however. Devs that install and import these component classes don‘t “own” them. Therefore, if they intend to extend them and use inheritance “from the outside” they will need to be very careful. Devs external to the system that are not in charge of version updates or potentially breaking internal changes should, at the very least, pin the version of the system classes on which they depend and make updating that version a very conscious activity. Design system devs will reserve the right to change the internals of their component classes in ways that external devs can‘t anticipate, so pinning the version is a requirement so that your code doesn‘t break unexpectedly.&lt;/p&gt;

&lt;p&gt;In general though, I love the intelligent use of inheritance to make creating several similar components quickly and easily. If you design for this inheritance in advance, you can save yourself a lot of repeated or shared code and testing.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Brittle tests</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Wed, 25 Feb 2026 03:24:11 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/brittle-tests-2joa</link>
      <guid>https://dev.to/michaelwarren1106/brittle-tests-2joa</guid>
      <description>&lt;p&gt;It‘s that time again to dive back into a discussion I had at work a while ago and turn the debate loose on the internet. This article comes directly from a discussion my partner-in-crime Tech Lead and I were having in terms of the best way to support our design system consumers when testing their apps using our design system web components. Shocking no one, we had differing opinions on what constitutes a brittle test, though we both agreed we didn’t want our consumers writing them.&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%2F26qlfrrt984ic2zbjnre.gif" 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%2F26qlfrrt984ic2zbjnre.gif" alt="Gif of a wine glass shattering" width="497" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let’s get to the bottom of what brittle tests are, shall we?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spoiler: I still don’t know. After you skim this article, lets continue the discussion &lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;over on Bluesky&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is a brittle test?
&lt;/h2&gt;

&lt;p&gt;I think the simplest definition of a brittle test is that it fails when you don’t want it to, or when you don’t expect it to. We’ve all seen flaky tests that depend on third-party systems or APIs and sometimes those systems are down when we’re trying to run our tests and push releases to production. Its why there are whole companies devoted to mocking test data and whole testing strategies designed to help mitigate test failures caused by integrating disparate systems.&lt;/p&gt;

&lt;p&gt;But in my design system, we don’t really have any third-party dependencies or services, so the type of test we picture is pretty standard. We pictured devs pulling our design system components into their applications, then running unit tests and expecting their applications to behave properly with and around our web components. The fact that our design system is made of web components and not framework components is particularly relevant here.&lt;/p&gt;

&lt;p&gt;So let me explain the perspective that my coworker and I each had.&lt;/p&gt;

&lt;h2&gt;
  
  
  A test that you have to change when implementation details change
&lt;/h2&gt;

&lt;p&gt;My coworker’s idea of a brittle test is one that needs constant updating whenever implementation details change in the application. His idea of "brittleness" is that the test should only be testing the desired results, such as the proper text rendered to the screen without any knowledge of the particulars about how the text actually got rendered to the screen. His mindset centered on usage of the popular &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;testing-library&lt;/a&gt; package that everyone is using these days.&lt;/p&gt;

&lt;p&gt;The goal of &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;testing-library&lt;/a&gt; is that you can query your page for elements and text by such things as their accessible roles, their label text, their test ids, etc. The library itself is framework independent (ie, it runs on more than just react) and the goal is to remove the need to check implementation details like framework component state values in your tests and just test the effect of those state updates to the rendered page. Its a great approach and I love the library, but web components present a problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Querying the shadow root in a test
&lt;/h3&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%2Ftp2x7kzl6p5ksxm44w29.gif" 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%2Ftp2x7kzl6p5ksxm44w29.gif" alt="gif of prisoner behind bars saying " width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to execute a testing-library test, the testing-library way using a web component with shadow dom, the locators need to be able to query into the shadow dom looking for the elements being located. Testing library doesn’t solve this need by default, but the web components community has jumped in to provide a tool that enables shadow root queries in testing- library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/konnorrogers/shadow-dom-testing-library" rel="noopener noreferrer"&gt;Check out shadow-dom-testing-library&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks Konnor!&lt;/p&gt;

&lt;p&gt;So if you use that tool, your testing-library tests can query the shadow dom of your web components like any other light dom element and everything works! Your tests can just check to see if the desired text was rendered on the page and you can go about your day!&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing library has no knowledge of web components
&lt;/h3&gt;

&lt;p&gt;My coworker’s point of view is that using the shadow root tool with testing-library would make tests naturally not brittle, because the tests would not need to know anything about web components being used in order for devs to test their applications. Here’s an example test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&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;screen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shadow-dom-testing-library&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lets test some rendering&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByShadowRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByShadowLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Car Manufacturer/i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryAllByShadowTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test just queries elements on the page, checking shadow roots when needed. Therefore if the dev changed the button component to some other element, they would not necessarily have to change their tests. In my coworker’s mind, this approach makes said tests more stable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A test that breaks without the code changing at all
&lt;/h2&gt;

&lt;p&gt;My point of view about brittle tests is that a test is brittle if it can suddenly break unexpectedly without the code necessarily changing at all. I take issue with tests querying the shadow roots of web components at will because of the fact that those shadow root internals are considered "internal" and are subject to change in a non-breaking package bump kind of way. It is entirely possible that the internal structure of a web component changes in a patch version bump in a way that could completely break tests that were querying for specific elements in shadow dom. It only depends on exactly what the test was querying for.&lt;/p&gt;

&lt;p&gt;And there is no real way to determine what sort of queries can be used. Take another look at the simple button example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&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;screen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shadow-dom-testing-library&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lets test some rendering&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByShadowRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByShadowLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Car Manufacturer/i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryAllByShadowTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Querying by label text for a button element in a button component that presumably uses a button web component is probably pretty safe. There is not likely to be a reason to change the web component internals in such a way that there is no longer a label. So that test above is likely safe.&lt;/p&gt;

&lt;p&gt;But what if example from above was more like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&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;screen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shadow-dom-testing-library&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lets test some rendering&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&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;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByShadowRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByShadowLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Car Manufacturer/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// breaks if the selector is removed&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.element-where-the-text-is&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryAllByShadowTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example doesn’t just query by text, it also filters queried elements by a selector, in this case a CSS class. If the dev updates their web component and &lt;code&gt;.element-where-the-text-is&lt;/code&gt; no longer resolves to an element, or no longer resolves to the element where the text is, the test above would fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not all locator queries are the same
&lt;/h3&gt;

&lt;p&gt;Testing-library has a &lt;a href="https://testing-library.com/docs/queries/about" rel="noopener noreferrer"&gt;number of ways to query elements&lt;/a&gt;. You can query &lt;code&gt;ByRole()&lt;/code&gt;, &lt;code&gt;ByPlaceholderText&lt;/code&gt;, and &lt;code&gt;ByTestId&lt;/code&gt;. In my opinion, some of these queries are safer than others when talking about querying into "private" shadow root templates. Not all of the queries enable filtering by css selectors, but a lot of them do. Querying &lt;code&gt;ByRole&lt;/code&gt; checks the accessible role on an element for a match and does not offer a filter by selector. I think &lt;code&gt;ByRole&lt;/code&gt; is one of the safer ones, because it is unlikely that design system components would be shifting around accessibility aria roles in a way that breaks those queries. But still, it could happen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; also has &lt;a href="https://playwright.dev/docs/locators" rel="noopener noreferrer"&gt;locators&lt;/a&gt; similar to testing-library and the same issue applies&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using testable access properties instead of direct queries
&lt;/h3&gt;

&lt;p&gt;My idea for tests would take advantage of what I call Internal Access Properties instead of directly querying the shadow root. I &lt;a href="https://dev.to/blog/internal-access-props"&gt;wrote an article&lt;/a&gt; about them a while back, so give that a read for the details.&lt;/p&gt;

&lt;p&gt;The short version is that Internal Access Properties provide access to predicable elements in the shadow root that tests can depend on to always be accurate, and to not change across patch bumps. The drawback is that to use IAPs means that tests need to have some implementation details in them because the tests now "know" that a web component is involved with a particular API and particular properties. Here is an example of a test using IAPs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lets test some rendering&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="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Car Manufacturer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Car Manufacturer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this version, the test does not query the shadow root directly. Tests access web component host elements only, and interact with internal elements through the Internal Access Property &lt;code&gt;innerButton&lt;/code&gt; that is guaranteed to always return the actual &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; inside the &lt;code&gt;&amp;lt;x-button&amp;gt;&lt;/code&gt; web component inside the &lt;code&gt;&amp;lt;App /&amp;gt;&lt;/code&gt;. This test knows there is an element with an &lt;code&gt;innerButton&lt;/code&gt; property involved in rendering the text to the screen. If that property name were to change, or the web component switched out, the test would need to be updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The debate
&lt;/h2&gt;

&lt;p&gt;The debate was really about what makes a brittle test, and, more specifically, what testing recommendation should we give to our design system consumers?&lt;/p&gt;

&lt;p&gt;Should we advise them to use testing-library queries and the shadow root plugin so that their tests don’t have to know that web components are involved? If we do, then we expose them to the possibility of tests breaking unexpectedly as we iterate on our web component internals. That possibility entirely depends on what kinds of tests and queries devs will write.&lt;/p&gt;

&lt;p&gt;Or should we recommend that they not query shadow dom directly, use IAPs with a guaranteed API? If we do, we introduce implementation details into tests which would need to be refactored anytime the structure of IAPs change, or devs switch out one component for another. Not testing implementation details is a fantastic approach and the IAP version bends that rule a bit.&lt;/p&gt;

&lt;p&gt;Or is there a super secret third option my coworker and I are totally missing?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tell me what you think &lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;over on Bluesky&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>You probably don‘t need controlled forms</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 19 Oct 2025 13:58:23 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/you-probably-dont-need-controlled-forms-29mi</link>
      <guid>https://dev.to/michaelwarren1106/you-probably-dont-need-controlled-forms-29mi</guid>
      <description>&lt;p&gt;I‘m going to start off by saying that this article is aimed mostly at React developers and applications because React is where I have seen this pattern the most. I‘m sure that the other frameworks provide mechanisms where controlled forms can be implemented, but React is the one that I feel led the way in this pattern the most, starting well before &lt;a href="https:react.dev" rel="noopener noreferrer"&gt;their new docs site&lt;/a&gt; was first published in 2023.&lt;/p&gt;

&lt;p&gt;I don‘t intend this article to shame or blame anyone for the patterns they chose. My purpose is to inspire a little rethinking about forms you probably have in your applications today and maybe save you a little (or a lot) of code in your client-side bundle with a different idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a "controlled" form?
&lt;/h2&gt;

&lt;p&gt;The most succinct way to describe a controlled form is: a form in which either the form elements‘ values and/or their validation are controlled by the application state instead of by their own internal state. An “uncontrolled” form or field is one where the value/validity comes from its own internal state and not by application/component state.&lt;/p&gt;

&lt;p&gt;A practical example of a “controlled” form might be the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// some JSX react component&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;someState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSomeState&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&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;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;some form element&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&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;input&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;someState&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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="nf"&gt;setSomeState&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;value&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="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I‘ve seen code like this a ton. A form element whose value property (it actually matters in this case that its the property and not the value attribute) is bound to the value of some component state and an event handler on the input that sets that same state. When the user types in the input, the &lt;code&gt;input&lt;/code&gt; event fires, the component state is updated, and the component is re-rendered which renders (sometimes reuses) the &lt;code&gt;input&lt;/code&gt; with the new value. In a pure controlled world, the user might not see the character they typed until the re-render cycle has completed.&lt;/p&gt;

&lt;p&gt;The value in this approach is that your component state (or global state also I suppose) is the source of truth and your UI is a function of your state. React folks love that formula: &lt;code&gt;UI = f(state)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;React used to be heavily invested in this pattern, as shown by the fact that every example on the legacy &lt;a href="https://legacy.reactjs.org/docs/forms.html#controlled-components" rel="noopener noreferrer"&gt;Forms documentation page&lt;/a&gt; was a controlled input example just like the one above. I believe that this page full of controlled examples set a large amount of web developers down the path of thinking that extra JS libraries were needed for ALL forms instead of perhaps just for complex forms. But, as always, the web has changed and even if the "you need a library for that" concept used to be true, its certainly much less true these days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Values vs validation
&lt;/h2&gt;

&lt;p&gt;I think it is worth another few paragraphs of context setting to talk about the difference between an input's value and its validity when it comes to being “controlled” and “uncontrolled”. Technically it is possible to separate the value from validity in terms of the source of truth, though most applications I have seen tend to use the same approach for validity as they do for setting values.&lt;/p&gt;

&lt;p&gt;So the example from above isn‘t really complete because its missing field-level validation that we all need when building our UIs. The more correct the data we send to our backends, the better, right!?&lt;/p&gt;

&lt;p&gt;Controlled validation operates the same way controlled value setting does except that the application state is setting an error state/message instead of a value.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Controlled validation is where most of my heartburn with controlled forms comes from. Validation libraries are huge and not always necessary, but are assumed to be the default way to deal with forms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a more complete validation example, I‘ll turn to a library like the legacy React docs suggested I do with the &lt;a href="https://legacy.reactjs.org/docs/forms.html#fully-fledged-solutions" rel="noopener noreferrer"&gt;admittedly small section in the legacy docs about validation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I‘ll use the example from the homepage of &lt;a href="https://react-hook-form.com/get-started" rel="noopener noreferrer"&gt;react-hook-form&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-hook-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;formState&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useForm&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;onSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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="cm"&gt;/* "handleSubmit" will validate your inputs before invoking "onSubmit" */&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;some form element&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* include validation with required or other standard HTML validation rules */&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;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exampleRequired&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;required&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="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="cm"&gt;/* errors will return when field validation fails  */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&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;exampleRequired&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This field is required&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;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;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example shows using a &lt;a href="https://bundlejs.com/?q=react-hook-form%407.65.0" rel="noopener noreferrer"&gt;40 kB (14.7 kB gzip) library&lt;/a&gt; to ensure that an input is required, has an error message and the form won‘t submit if the input doesn’t have a value. Guess what? Browsers do that for you for free nowadays. In fact, the browser would have calculated the input to be invalid before the event listeners upon which &lt;code&gt;react-hook-form&lt;/code&gt; depends even ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when don‘t you need controlled forms?
&lt;/h2&gt;

&lt;p&gt;Ok, you might need controlled forms sometimes. But I would venture a guess that a large, large majority of the forms on your sites and apps simply gather data to send to some backend. If that is all you are doing, you probably definitely don‘t need application state at all.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application/component state binding is only necessary if your component needs to re-render for reasons OTHER than the form data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The implication of the examples in this article so far are that you need to wire up event listeners to your form elements to update your component/application state in response to the user typing. But notice that in all of these examples, &lt;em&gt;there‘s nothing else going on in the component that would require re-rendering it&lt;/em&gt;. That component state we‘re taking great care to initialize and update is only being used for the form itself. If that is all your component is doing, then the entire event emit cycle and state update is completely unnecessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter FormData
&lt;/h3&gt;

&lt;p&gt;Let me introduce you to the built-in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData" rel="noopener noreferrer"&gt;FormData&lt;/a&gt; object that operates exactly like your component state except the browser keeps it updated completely automatically without the need for excess component state and re-renders. Let‘s look at how you could take the &lt;code&gt;react-hook-form&lt;/code&gt; example in this article and convert it to use &lt;code&gt;FormData&lt;/code&gt; for values and browser default validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the event target is the form itself&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&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;FormData&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="c1"&gt;// this is the "send the data to some API" part&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="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;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;some form element&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* preventing the `invalid` event turns off the browser default validation message because we have our own */&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;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"some-name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onInvalid&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"error-message"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This field is required&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.error-message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* hidden by default */&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:user-invalid&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;.error-message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&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;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;Tell me what you think about this approach&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And there we go. No libraries, no unnecessary &lt;code&gt;useState&lt;/code&gt; and component re-render. The browser tracks both the value of the input, and its validity state automatically. There‘s no need to sync the form state with our component state because there's nothing else in this component except our form. The form will not submit when the input doesn’t have a value because we used the browser default &lt;code&gt;required&lt;/code&gt; attribute. The &lt;code&gt;:user-invalid&lt;/code&gt; css style is applied when the user interaction causes an invalid state (you could use &lt;code&gt;:invalid&lt;/code&gt; as well if that is a better fit). If we had multiple fields with multiple errors, our &lt;code&gt;:has&lt;/code&gt; selector on the form would show them individually when the user tabs through, or all at once when the user tries to submit. We have all the same functionality of the &lt;code&gt;react-hook-form&lt;/code&gt; example, with half the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  A word about browser constraint validation
&lt;/h3&gt;

&lt;p&gt;What you don‘t see in my example is some of the magic that the browser has built-in for you. That is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation" rel="noopener noreferrer"&gt;Constraint validation API&lt;/a&gt; that is making sure that the form won‘t submit with invalid fields. There are more &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation#validation-related_attributes" rel="noopener noreferrer"&gt;validation attributes&lt;/a&gt; than just &lt;code&gt;required&lt;/code&gt; and they represent practically all of the simple validation that could be done on a form field of practically any type.&lt;/p&gt;

&lt;p&gt;Each HTML form element implicitly has a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity" rel="noopener noreferrer"&gt;validity&lt;/a&gt; property that holds a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ValidityState" rel="noopener noreferrer"&gt;ValidityState&lt;/a&gt; object that tells you the validity of the field and which of the constraints are invalid if any. If you ever want to check the validity of a field, or cause a form to do validation, you can call the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/checkValidity" rel="noopener noreferrer"&gt;checkValidity&lt;/a&gt; yourself anytime to programmatically make errors appear or to check the form state yourself.&lt;/p&gt;

&lt;p&gt;In fact, some of the better validation libraries are built on the native Constraint Validation API, so if you get to a place where you need to pull in a library, double check that the authors aren’t rolling their own validation. If they aren’t using the built-in validation, they are probably wasting code.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when would you need a controlled form?
&lt;/h2&gt;

&lt;p&gt;I admit that my examples are the most basic form possible and that there are plenty of complex apps out there. And I also admit that part of my writing this article is to make folks reconsider how they write forms in an effort to simplify them. However, you may still need to sync an input‘s value with your component state if there is some other part of your component that would need to change based on that state. Some examples of when it might make sense to use events and &lt;code&gt;useState&lt;/code&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hiding/showing parts of a form based on user selection&lt;/li&gt;
&lt;li&gt;Changing the values of a future field based on the selection of a previous field&lt;/li&gt;
&lt;li&gt;Enabling/disabling fields conditionally&lt;/li&gt;
&lt;li&gt;Making a field required conditionally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, does that mean that your application state should be &lt;em&gt;in charge&lt;/em&gt; of your form state the way that legacy react docs describe? I’m not so sure. All of the above examples can be done with event listeners and &lt;code&gt;useState&lt;/code&gt; without tying the &lt;code&gt;value&lt;/code&gt; of any field to that same state, so I’m unsure if binding the &lt;code&gt;value&lt;/code&gt; to application state is all that necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Last thoughts
&lt;/h2&gt;

&lt;p&gt;I also think that the React team may have reconsidered as well, since the first example of reading data from a form on &lt;a href="https:react.dev" rel="noopener noreferrer"&gt;react.dev&lt;/a&gt; is an &lt;a href="https://react.dev/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form" rel="noopener noreferrer"&gt;“uncontrolled” example&lt;/a&gt;. And they have written more documentation about the ramifications of “controlled” forms and the &lt;a href="https://react.dev/reference/react-dom/components/input#optimizing-re-rendering-on-every-keystroke" rel="noopener noreferrer"&gt;trouble they can cause&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And you can also check out &lt;a href="https://youtu.be/aGkscOKWQvQ?t=720" rel="noopener noreferrer"&gt;this great talk by David Khourshid&lt;/a&gt; on his quest to get rid of &lt;code&gt;useState&lt;/code&gt; in general. He talks at length as well about how the ”controlled” form pattern should probably be a last resort instead of the first approach.&lt;/p&gt;

&lt;p&gt;It might be worth taking a good long look at the forms in your application and separating out simple from complex. Be mindful of whether you’re setting component state only for forms and fields or if there are other elements that depend on that state. If the form stands alone, then you can save yourself a ton of code by using browser defaults. You may indeed be able to get rid of that hefty validation library and save your user some bytes down the wire as well!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automatic dark mode</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 19 Oct 2025 13:55:34 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/automatic-dark-mode-4jbi</link>
      <guid>https://dev.to/michaelwarren1106/automatic-dark-mode-4jbi</guid>
      <description>&lt;h2&gt;
  
  
  What we need to accomplish
&lt;/h2&gt;

&lt;p&gt;We have a site with both a light and dark mode for several themes and we need to implement how our site can easily switch between modes without a lot of headache.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dark and light are “modes” not “themes”. See my &lt;a href="https://michaelwarren.dev/blog/dark-mode-rant/" rel="noopener noreferrer"&gt;rant&lt;/a&gt; about modes and themes for more info on why.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I think we should call this feature “mode-switching”, and let‘s talk about the most straightforward way to implement it. We‘ll need to make use of some relatively modern CSS but the result will be worth it.&lt;/p&gt;

&lt;p&gt;Let‘s start with the assumption that we already have some design tokens for colors—shadows might apply in dark mode also, but mostly colors are what change across modes. And you already have some mechanism that enables switching between modes. That mechanism can just be that your tokens obey the browser setting only, or you can also use some css selector and mode switcher button in your application that applies the mode regardless of the state of the browser setting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This site has a mode switch button and a separate theme selector in the top right corner because modes and themes are not the same. :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Either way, what we‘re trying to implement is that a component on a page will automatically adopt whatever mode the page around it is set to use. No need to add extra classes, set javascript properties, or add extra media queries inside components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design tokens setup
&lt;/h2&gt;

&lt;p&gt;In order to pull off automatic mode switching, our design tokens in CSS are going to need to be formatted correctly. In fact, the magic is entirely in the CSS of your design tokens. The rest of the page, components and all, just use the design tokens as intended.&lt;/p&gt;

&lt;p&gt;Here is the basic structure for design tokens that enables automatic mode switching.&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;/* product-a-theme.css */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-brand-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light-dark&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="no"&gt;lightblue&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* light mode, dark mode */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;light-dark()&lt;/code&gt; CSS function evaluates the currently active value of the &lt;code&gt;color-scheme&lt;/code&gt; property for an element and then resolves to the correct value accordingly. When &lt;code&gt;color-scheme&lt;/code&gt; is &lt;code&gt;light&lt;/code&gt;, the first value passed to &lt;code&gt;light-dark()&lt;/code&gt; is used. When &lt;code&gt;color-scheme&lt;/code&gt; is &lt;code&gt;dark&lt;/code&gt;, the second value is used instead.&lt;/p&gt;

&lt;p&gt;The example above is a bit incomplete, though, because we haven’t told the browser what &lt;code&gt;color-scheme&lt;/code&gt; we want to be active. There are a few ways to tell the browser what color scheme to use. We‘ll build up to a complete example feature by feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change color-scheme according to browser setting
&lt;/h3&gt;

&lt;p&gt;If we only want to set the mode according to what the browser setting is, we can add the following CSS to our design tokens theme file.&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="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;no-preference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* use "dark" as the default if your site defaults to a dark mode */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&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;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;prefers-color-scheme&lt;/code&gt; setting is the browser setting we‘re acting upon and the media query lets us change CSS depending on the value of that setting.&lt;/p&gt;

&lt;p&gt;The media queries above will change the &lt;code&gt;color-scheme&lt;/code&gt; property for the whole page depending on the value of the browser setting for whatever mode the user prefers and will default to light mode if the user has not set a preference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change color-scheme with a selector
&lt;/h3&gt;

&lt;p&gt;Having the &lt;code&gt;color-scheme&lt;/code&gt; property and mode of your site obey browser settings is great, but that’s not necessarily the end of the story. Users like to have fine-grained control over the sites they use. There might be a reason or a situation in which they which to use your site in a mode that contradicts their browser preference.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Letting users contradict their browser setting for color-scheme is a great accessibility win. A simple example is that its easier to read a light mode site in bright sun. Someone that normally likes dark mode might want to temporarily switch to light mode if checking out your site on a sunny day.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So giving users a mode switcher button in addition to obeying browser settings is an accessibility win and a better user experience than just respecting browser settings alone.&lt;/p&gt;

&lt;p&gt;So assuming that there is a mode switcher button in your application UI somewhere—one of those fancy animated SVG ones with a sun and moon—that can toggle a selector on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element, we need more CSS to handle the case that the user has clicked the mode switch button.&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;/* the data-mode attribute goes on the `&amp;lt;html&amp;gt;` tag and is toggled by a button somewhere */&lt;/span&gt;

&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;I find that attribute selectors—with "data-" or without—work better than classes for setting "state" in CSS where there can only be one state active at a time. Adding and removing classes is more complicated than just changing the value of an attribute, and the value of an attribute selector is going to more closely align with your application‘s internal state better than a class name will.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using both the media query and the &lt;code&gt;data-mode&lt;/code&gt; attribute creates an order of preferences in your application. The &lt;code&gt;[data-mode]&lt;/code&gt; attribute selector is higher specificity than the &lt;code&gt;:root&lt;/code&gt; selector alone. So, if your application add or sets the &lt;code&gt;data-mode&lt;/code&gt; attribute, the application will respect the users choice regardless of what their browser settings for dark mode are because the &lt;code&gt;data-mode&lt;/code&gt; CSS rules will be applied.&lt;/p&gt;

&lt;p&gt;Likewise, if you only want to respect browser choice and not provide a secondary mode switcher button in your application, then the browser choice is respected automatically as soon as the media query is evaluated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;

&lt;p&gt;So for a “bulletproof” theme setup we would combine all of these approaches into our single theme CSS file as such:&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;/* product-a-theme.css */&lt;/span&gt;

&lt;span class="c"&gt;/* BROWSER SETTING */&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;no-preference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* use "dark" as the default if your site defaults to a dark mode */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&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;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* USER INTERACTION */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* TOKENS */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-brand-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light-dark&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="no"&gt;lightblue&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* light mode, dark mode */&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 repeat this structure as needed for Product B with green values for the color tokens, with variants for both light and dark mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just use the tokens normally
&lt;/h2&gt;

&lt;p&gt;All that is left is for some component to use the tokens as intended. So you will have some component style like:&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;/* header.css */&lt;/span&gt;

&lt;span class="nt"&gt;header&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;--color-brand-primary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your header color will automatically switch modes without any extra JS!&lt;/p&gt;

&lt;h2&gt;
  
  
  This approach is also great for web components
&lt;/h2&gt;

&lt;p&gt;Another aspect that makes the above approach "bulletproof" is that is also works with web components and shadow dom!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// I use Lit. Lit is great. This example is a Lit example, but you can do something similar in vanilla JS also&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;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="s2"&gt;`
    p {
      color: var(--color-brand-primary, blue);
    }
  `&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;p&amp;gt;I will automatically change colors when the mode switches and tokens are defined.&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you go! Automatic mode switching in a web component!&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%2Fj74bcmicm9ncf4s6dizw.gif" 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%2Fj74bcmicm9ncf4s6dizw.gif" alt="Actor Michael Sheen blowing out a lit candle" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CSS Custom property here is doing all the heavy lifting. CSS custom properties inherit through the shadow root boundary, so they can be defined outside the shadow root of a web component and used inside shadow root styles just fine.&lt;/p&gt;

&lt;p&gt;The value of the CSS property resolves with respect to the &lt;code&gt;color-scheme&lt;/code&gt; of the outside page, so the web component styles don’t even need to “know” what &lt;code&gt;color-scheme&lt;/code&gt; is set. The design token CSS custom properties handle everything. No javascript, no extra code, just a sensible, robust design token setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  My personal recommendation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Combine your light and dark mode variants into a single design tokens theme file&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have seen a lot of variations on how to accomplish multiple modes on a website. It seems to be a popular approach to have two theme files, one for light and one for dark mode and to switch them out with Javascript when the user changes the them, or when browser setting is detected.&lt;/p&gt;

&lt;p&gt;I like the approach of using &lt;code&gt;light-dark()&lt;/code&gt; and &lt;code&gt;color-scheme&lt;/code&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No extra Javascript is needed to switch token values&lt;/li&gt;
&lt;li&gt;No need to worry about hosting and loading a second file&lt;/li&gt;
&lt;li&gt;The size of a single file with &lt;code&gt;light-dark()&lt;/code&gt; is less than the combined file sizes of two files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once your mode switching gets more complicated, as in you decide one day to save the mode in &lt;code&gt;localStorage&lt;/code&gt; or something, it‘ll be nice that you don‘t also have to juggle requesting and loading more CSS files. Your mode switcher button can focus solely on changing the CSS selector for the mode the user wants and/or saving the choice somewhere.&lt;/p&gt;

</description>
      <category>css</category>
      <category>design</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A quick rant about dark mode</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 19 Oct 2025 13:54:38 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/a-quick-rant-about-dark-mode-1oe5</link>
      <guid>https://dev.to/michaelwarren1106/a-quick-rant-about-dark-mode-1oe5</guid>
      <description>&lt;p&gt;I need to get a pedantic little rant out of my system. I‘ve seen a lot of conversations out there on the internet about application theming and dark mode. A lot of folks seem to conflate dark mode with theming and I cannot help that it gets under my skin. So consider this little rant to be my first small attempt to get folks to change the way they talk about dark mode and theming.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A dark option to your branding theme is a “mode” not a “theme”. Especially when that option is backed by a browser setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is a theme?
&lt;/h2&gt;

&lt;p&gt;In my mind, a “theme” is a set of design token values that implement a single expression of a brand aesthetic. If you work at a smaller company, you might only have one brand look and feel, so you might only have one theme. If you work at a larger company or one that serves as an umbrella for a variety of brands, you might have several themes.&lt;/p&gt;

&lt;p&gt;TO explain further, let‘s say you are building a design token library for a company with several products and each product has its own color scheme and branding style. Product A has a blue color scheme, lots of spacing, and pill buttons, and Product B has a green scheme, more compact spacing, and rounded rectangle buttons. You are a brilliant design system creator, so you make an amazing set of semantic design token names and you need to set token values for Product A and B. The set of token values you create for Product A with the blue color scheme is a theme. Likewise the set of token values you create for Product B is a different theme. If both themes default to light colors, then that can optionally be called "light mode" for each theme, and you can optionally make a dark mode variant for each theme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why dark mode isn‘t a theme
&lt;/h2&gt;

&lt;p&gt;The crux of the terminology choice for me is that light and dark style should still obey the branding concepts and principles for each product. They are not their own themes, they are variations on a theme. Ask a musician if you know one, variations on a theme are all over the place in classical music. If there wasn‘t a browser setting that controlled light and dark mode preference, I‘t insist we use the term “variant”, but since we do have a browser setting, let‘s all agree to call light and dark variations of a theme "modes" from now on?&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%2Fqiuf2miem1yh32lpvyc7.gif" 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%2Fqiuf2miem1yh32lpvyc7.gif" alt="Edna Mode from “The Incredibles” saying " width="500" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;^ That gif makes me want to make an "Edna Mode" variant of one of my themes ;P&lt;/p&gt;

&lt;h2&gt;
  
  
  What if I only have a theme with dark colors?
&lt;/h2&gt;

&lt;p&gt;Well then you have a theme with only a single mode and that mode happens to be dark.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this matter?
&lt;/h2&gt;

&lt;p&gt;Only because words mean things and I‘m more than a little bit pedantic about terminology. It drives me crazy to see &lt;code&gt;&amp;lt;html data-theme="dark"&amp;gt;&lt;/code&gt; on a site, but that convention seems relatively pervasive. We all call it “dark mode” when we talk about the feature we‘re implementing, then go immediately slap a &lt;code&gt;data-theme=""&lt;/code&gt; on our &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; and completely ignore the fact that we called it a “mode” 10 seconds before we wrote the code.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://chromewebstore.google.com/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh?hl=en-US" rel="noopener noreferrer"&gt;Dark Reader&lt;/a&gt; chrome extension use both “mode” and “theme” in the first two sentences of their description and they are describing the same thing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Mode” and “theme” are not interchangeable&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Popular articles on &lt;a href="https://css-tricks.com/a-dark-mode-toggle-with-react-and-themeprovider/" rel="noopener noreferrer"&gt;CSS-Tricks&lt;/a&gt; talk about implementing “A Dark Mode Toggle with React ThemeProvider”. There we go again mixing terms.&lt;/p&gt;

&lt;p&gt;The idea that “mode” and “theme” are interchangeable is everywhere. I‘d prefer that we establish a separation between the two terms so that we can more properly define what roles themes and modes play and more properly implement them.&lt;/p&gt;

&lt;p&gt;/rant&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>When to emit events for stateful components</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 19 Oct 2025 13:53:21 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/when-to-emit-events-for-stateful-components-17ni</link>
      <guid>https://dev.to/michaelwarren1106/when-to-emit-events-for-stateful-components-17ni</guid>
      <description>&lt;p&gt;This post represents a debate that I had at work in order to make some decisions about how a component should emit its custom events and how we would holistically handle event emission across all of our components.&lt;/p&gt;

&lt;p&gt;I am seriously considering titling my blog as "Stuff I debate with my coworkers about" because its very much starting to feel like that‘s where I get most of the content I want to write about.&lt;/p&gt;

&lt;h2&gt;
  
  
  State changes that require emitting events can be tricky
&lt;/h2&gt;

&lt;p&gt;Web components have state (attributes and properties). That state can change. Those state changes can be driven by developers setting attributes and/or properties (unlike framework-land, web-component-land has both) and by users interacting with the component in a way that causes that state to change.&lt;/p&gt;

&lt;p&gt;The example at hand is a user clicking a button that closes a dialog or dismisses an alert. Let‘s say you have a web component for your alert, that has an &lt;code&gt;open&lt;/code&gt; prop—typing "attribute/property gets tiresome, so from now on I’ll just say "prop". So the user clicking that button when the alert is visible is some click handler like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handleClick() {
  // user clicks button
  this.open = false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our developer consumers need to know when the alert is dismissed so they can do some fancy analytics tracking, or fetch some data or something. So we need to emit an event signaling "Hey code, the alert got dismissed."&lt;/p&gt;

&lt;p&gt;But your developer consumers can also use that same property programmatically to force the alert closed for some reason. Maybe the user clicked the reset button on the form the alert is next to so we need to dismiss the alert but the user didn't do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preventable events
&lt;/h3&gt;

&lt;p&gt;Another piece of context that this debate requires is that the dismiss event for the alert is preventable. I wrote &lt;a href="https://dev.to/blog/preventable-events/"&gt;an article a few years ago&lt;/a&gt; that goes into detail about how to implement preventable events to keep your component stateful (uncontrolled) but still allow for your dev consumers to take control of the component if the need arises.&lt;/p&gt;

&lt;p&gt;So that means our click handler for the dismiss button of our alert looks like something more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handleClick() {
  // user clicks button

  const event = this.emitPreventableEvent('dismiss');

  if(!event.defaultPrevented) {
    this.open = false;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the event is preventable, we only do the state change automatically if the dismiss never has its &lt;code&gt;.preventDefault()&lt;/code&gt; method called on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The crux of the debate
&lt;/h2&gt;

&lt;p&gt;So as of now our user action is handled perfectly. Our end user can click the dismiss button, our dev consumer gets an event to listen to for their fancy analytics, and everyone is happy. Until the developer needs to programmatically set &lt;code&gt;open&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; themselves.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Question:&lt;br&gt;
Do we still emit the event when programmatic state changes occur?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I see it, there are a couple of options that we could choose from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No, don‘t emit events on programmatic state changes&lt;/li&gt;
&lt;li&gt;Yes, always emit events, even for programmatic state change&lt;/li&gt;
&lt;li&gt;Super secret third option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I‘ll get to what my choice was and what our group has settled on as our approach going forward later on. Feel free to skip ahead to the decision section. If you‘re not skipping, here‘s the pros and cons of the first two options as I see them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Don‘t emit events for programmatic state changes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pros&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Devs easily know that events are only generated by users&lt;/li&gt;
&lt;li&gt;Easier to write event handlers if user generated events are mostly what devs care about using&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trickier to implement in the component, because your event emitting logic needs to be conditional&lt;/li&gt;
&lt;li&gt;Harder to document because events are conditional and those conditions need to be described and kept accurate if they change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option 2: Always emit events even for programmatic state changes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pros&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component logic is simpler because event emitting isn‘t conditional&lt;/li&gt;
&lt;li&gt;Easier to document because events are not conditional&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trickier for devs to figure out if an event was user-generated or not&lt;/li&gt;
&lt;li&gt;Preventable events have infinite loop possibilities if not implemented correctly*&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Preventable event infinite loop scenario
&lt;/h3&gt;

&lt;p&gt;In the case that you‘ve read &lt;a href="https://dev.to/blog/preventable-events/"&gt;my article on preventable events&lt;/a&gt;, think I‘m a genius and have started using them as a pattern, you‘ll know that the point of preventable events is for developers to be able to take programmatic control over an otherwise automatic state change the component usually does on its own. If you also decide that events are always generated, even by programmatic control, then you'll get something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks button&lt;/li&gt;
&lt;li&gt;Preventable event is emitted&lt;/li&gt;
&lt;li&gt;Handler is called and has &lt;code&gt;event.preventDefault()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt; is programmatically set to false after fancy analytics&lt;/li&gt;
&lt;li&gt;Preventable event is omitted&lt;/li&gt;
&lt;li&gt;Handler is called and has &lt;code&gt;event.preventDefault()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt; is programmatically set to false after fancy analytics&lt;/li&gt;
&lt;li&gt;Preventable event is omitted&lt;/li&gt;
&lt;li&gt;Handler is called and has &lt;code&gt;event.preventDefault()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt; is programmatically set to false after fancy analytics&lt;/li&gt;
&lt;li&gt;Preventable event is omitted&lt;/li&gt;
&lt;li&gt;Handler is called and has &lt;code&gt;event.preventDefault()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt; is programmatically set to false after fancy analytics&lt;/li&gt;
&lt;li&gt;Preventable event is omitted&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...the page crashes.&lt;/p&gt;

&lt;p&gt;In order to prevent the infinite loop, some sort of external state must be tracked in order to decide whether or not to call &lt;code&gt;event.preventDefault()&lt;/code&gt; and we‘re right back to the main negative which is that its hard to figure that out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do all our friends do?
&lt;/h2&gt;

&lt;p&gt;We‘re lovers of a few different web component libraries, so naturally we checked out &lt;a href="https://shoelace.style/" rel="noopener noreferrer"&gt;Shoelace&lt;/a&gt; and &lt;a href="https://webawesome.com/" rel="noopener noreferrer"&gt;Web Awesome&lt;/a&gt;–same thing I know–to see what they do. They basically always emit events and some components can be tricky to programmatically operate depending on the event type. More good friends of ours over at &lt;a href="https://opensource.adobe.com/spectrum-web-components/index.html" rel="noopener noreferrer"&gt;Adobe Spectrum Web Components&lt;/a&gt; seem to only emit events on user generated state change. I checked &lt;a href="https://learn.microsoft.com/en-us/fluent-ui/web-components/" rel="noopener noreferrer"&gt;Fluent UI web component also&lt;/a&gt; but couldn‘t find a great example of this use case, so who knows what they‘ll end up doing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;I‘ll tell you my thoughts after you tell me yours&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where we ended up
&lt;/h2&gt;

&lt;p&gt;We ended up with super secret option 3 which is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always emit events for state changes that need them, but preventable events should be conditionally preventable based on their origin.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This decision is a bit tricky to explain, so I‘ll show some code (in &lt;a href="https://lit.dev" rel="noopener noreferrer"&gt;Lit&lt;/a&gt; because that‘s what I like and use daily.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;The code samples I showed before were a lie. Well, they were simplified. Our helper function for emitting events takes the event name, all the event options (bubbles, cancelable, composed, etc) and a callback function to execute for cancelable state changes. The helper function orders the state change callback depending on whether or not the event is cancelable–a cancelable event‘s &lt;code&gt;preventDefault()&lt;/code&gt; method works, non-cancelable events &lt;code&gt;preventDefault()&lt;/code&gt; methods don‘t. If the event being emitted is cancelable, the state change is run before the event so event handlers have the most update to date version of component state, otherwise for cancelable events the state change is only run if the event isn‘t prevented and happens after all event handlers are run.&lt;/p&gt;

&lt;p&gt;Also, we combine the implementation of the open property with the button click so that both actually call the same function. So our code actually looks more like this:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="nf"&gt;handleDismissClick&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="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;dismissAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dismiss-button&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="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;dismissAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dismiss-button&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;click-away&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dismiss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// there's also a detail object where we provide the current value of `open` and the next/new one&lt;/span&gt;
    &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
      &lt;span class="c1"&gt;//state changes, before event if not cancelable, after event if cancelable and not prevented&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;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Lit lifecycle function, runs before the component is re-rendered&lt;/span&gt;
&lt;span class="c1"&gt;// property values changed here don't cause another re-render&lt;/span&gt;
&lt;span class="nf"&gt;willUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open)) {
    if(this.open) {
      this.#openAlert();
    } else {
      this.#dismissAlert();
    }
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this implementation our new flow for the preventable dismiss event looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks button&lt;/li&gt;
&lt;li&gt;Preventable event is emitted&lt;/li&gt;
&lt;li&gt;Handler is called and has &lt;code&gt;event.preventDefault()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open&lt;/code&gt; is programmatically set to false after fancy analytics&lt;/li&gt;
&lt;li&gt;Non-preventable event is omitted&lt;/li&gt;
&lt;li&gt;Automatic state change sets &lt;code&gt;open&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;event.preventDefault()&lt;/code&gt; in the event handler is no longer functional, but fancy analytics can still run&lt;/li&gt;
&lt;li&gt;Programmatic setting of &lt;code&gt;open&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; in the event handler doesn‘t cause a re-render because &lt;code&gt;open&lt;/code&gt; is already false&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://bsky.app/profile/michaelwarren.dev" rel="noopener noreferrer"&gt;Chat with me about this approach&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Is this implementation the best of both worlds? I have no idea! But I like it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Events are always emitted&lt;/li&gt;
&lt;li&gt;Developers can use &lt;code&gt;event.cancelable&lt;/code&gt; in handlers for conditional logic if they need to&lt;/li&gt;
&lt;li&gt;Cancelable events are only emitted on user actions&lt;/li&gt;
&lt;li&gt;We can predictably document the component‘s behavior and it wont be as conditional and subject to change&lt;/li&gt;
&lt;li&gt;No infinite loops!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>webcomponents</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Public CSS Custom Properties in the Shadow DOM</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Sun, 19 Oct 2025 13:46:04 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/public-css-custom-properties-in-the-shadow-dom-4hc6</link>
      <guid>https://dev.to/michaelwarren1106/public-css-custom-properties-in-the-shadow-dom-4hc6</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Naturally there is a lot to talk about with respect to styling custom elements and web components. This article won‘t dive into all the ways to style a component and/or open up stylistic configuration of your web components. This article is going to focus specifically on using CSS Custom properties and things to think about when you create them for your component consumers or when you are using them internally in your components.&lt;/p&gt;

&lt;p&gt;There are two main categories of usage to discuss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public CSS properties meant for usage externally by consumers&lt;/li&gt;
&lt;li&gt;"Private" CSS properties meant to be used internally to make styles easier to implement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are lots of articles already describing private CSS variables and naming schemes and such, so this article will focus on the public side of CSS properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Public CSS properties
&lt;/h2&gt;

&lt;p&gt;I think the first thing to think about is why and when to start exposing CSS properties to your consumers in the first place. There is of course a spectrum of component needs and use cases, and a spectrum of configuration that you want to present your users as the component author. But I hope that we can agree that components are best when there is some sort of way to tailor them to a use case that you the amazing component author forgot to think about even though you‘re great.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to reach for public CSS Custom properties
&lt;/h3&gt;

&lt;p&gt;There are a gazillion (technical term for "a lot") of ways to provide configuration for the components you build and you can pick and choose the ones that feel best to you depending on your component, your consumer audience, and your personally philosophy about how components should operate.&lt;/p&gt;

&lt;p&gt;If you‘re a progressive enhancement person and you code your components such that they present a nice experience even when JS isn‘t loaded yet, you might opt for CSS properties because they don‘t necessarily need JS to work. If you‘re a "JS is required on the web anyway" type person then you might opt more often for JS component property configuration. Here‘s how I think about it, and how I recommend you do too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My personal advice:&lt;/strong&gt;&lt;br&gt;
Don‘t reach for JS when CSS will do the trick.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the thing you‘re configuring in your component is a CSS property anyway, stick to CSS to configure it even if there are design-system-y standard values you want to stick to. CSS is simpler and less constricting which your consumers will love as soon as they run into a valid use case you didn‘t code for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decide who wins between component styles and external properties
&lt;/h3&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%2Fcfk2hms3nb7ebwqowzo8.gif" 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%2Fcfk2hms3nb7ebwqowzo8.gif" alt="American football referee signalling a false start penalty." width="480" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inevitably you will come across a feature where you want to expose a custom CSS property to configure it, but the component will be weird if the user gets it wrong. Accessibility is a great reason why these use cases happen. The age old "I don‘t know what alt text this image needs, so I give you a way to add one, but you need to make it meaningful else it sucks" situation.&lt;/p&gt;

&lt;p&gt;When that happens, you will need to decide if you will take control and prevent the behavior by way of your styles, or if you will let the external style win and potentially be a negative experience.&lt;/p&gt;

&lt;p&gt;Take this situation as an example:&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* public css property */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-animating-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(prefers-reduced-motion)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to accomplish my accessibility feature of "the component automatically slows down the fast animation", I needed to completely override the incoming value of the CSS custom property I said was usable for configuration.&lt;/p&gt;

&lt;p&gt;In my opinion, component authors should decide if a public CSS property's value is "absolute" in that the component will ALWAYS take that value even if its bad or if CSS properties are conditionally applied like the above in order to preserve some feature.&lt;/p&gt;

&lt;p&gt;That decision has ramifications both in documentation (you have to write down when the CSS property works and when it doesn‘t) and in implementation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My personal advice:&lt;/strong&gt;&lt;br&gt;
Public CSS properties should be absolute.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you provide a public configuration option to the user, its simpler and more consistent with other forms of configuration if public CSS properties just always win. This might be a spicy take, but I don‘t recommend doing a ton of runtime checking of JS property values either. If the consumer sets a property to a bad value, the component does weird things. Assume positive intent, write great docs, and the other stuff is on your consumers.&lt;/p&gt;

&lt;p&gt;Here's a way of implementing the above example with an "absolute" public CSS property:&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* public css property */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-animating-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the media query is missing? That's another choice you have when you decide that public CSS properties always win. If you are tempted to overwrite the public CSS property value and you decide not to, where does that media query go? One answer is to let your consumer account for that &lt;code&gt;prefers-reduced-motion&lt;/code&gt; case if they need/want to knowing that some of them won‘t do the right thing all the time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Taking a public CSS property into account
&lt;/h4&gt;

&lt;p&gt;"But I want to keep the media query so that I know the component is accessible" you say? Sure you do, you‘re an awesome component developer that thinks about accessible components. So here‘s a hybrid example where the public CSS property value is "taken into consideration but ultimately overwritten".&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* public css property */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-animating-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(prefers-reduced-motion)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ultimately the actual &lt;code&gt;animation-duration&lt;/code&gt; the component used wasn‘t the one the consumer set. The &lt;code&gt;calc()&lt;/code&gt; takes that value into account and does some accessible math, and you totally wrote that into your docs! But now have to keep track of that &lt;code&gt;2s&lt;/code&gt; in your docs and change it when the &lt;code&gt;calc()&lt;/code&gt; changes so that your docs stay accurate. Ever forget to update something like that? Never happen, right? Right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actually implementing public CSS properties
&lt;/h3&gt;

&lt;p&gt;Ok, so you‘ve decided on the configuration and you‘ve aligned your outlook on public CSS properties. Now you get to implement it! There are a few things to keep in mind when you have a CSS property that is designed to be used externally. We've sneakily already covered the first one&lt;/p&gt;

&lt;h4&gt;
  
  
  Make sure you don't overwrite the value
&lt;/h4&gt;

&lt;p&gt;In the world where you agree with me completely and you‘ve decided that your CSS properties are absolute winners and the component will always respect the value of them even if they are weird and bad, then you should take care where and how you define those properties so they don‘t get overwritten.&lt;/p&gt;

&lt;p&gt;Making sure to not overwrite the value depends on one key aspect: &lt;strong&gt;whether or not the property has a component-specific default value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you want your component to inherit a design system token that is defined globally in some high up stylesheet relative to the component that‘s totally doable, but a different implementation because the value isn‘t component-specific.&lt;/p&gt;

&lt;h5&gt;
  
  
  Default value is "global"
&lt;/h5&gt;

&lt;p&gt;For components with global stylesheets with design system tokens, you‘ll want to take care to only USE the CSS property and not DEFINE its value in your shadow DOM styles.&lt;/p&gt;

&lt;p&gt;The first example "consumes" a globally defined token (generally defined on &lt;code&gt;:root&lt;/code&gt;) and add fallback values to account for the global tokens not being 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="c"&gt;/* some shadow root stylesheet */&lt;/span&gt;
&lt;span class="nc"&gt;.some-class&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-global-token&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="c"&gt;/* 4px will only be used if the token isnt defined */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All globally/generically defined tokens can inherit through the shadow DOM boundary and will apply to your component as long as they are only used and not redefined.&lt;/p&gt;

&lt;p&gt;The next example shows what redefining the token in component styles looks like.&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;/* some shadow root stylesheet */&lt;/span&gt;

&lt;span class="c"&gt;/* redefines the value and ignores a global token on :root */&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;--my-global-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-class&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-global-token&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="c"&gt;/* 10px */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we‘re redefining the value in &lt;code&gt;:host&lt;/code&gt; so the token value from &lt;code&gt;:root&lt;/code&gt; won‘t cascade. The value can still be changed externally, but your consumers will need to apply the component‘s tag name or some other specific selector to override the &lt;code&gt;10px&lt;/code&gt; value declared in &lt;code&gt;:host&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here‘s what it would look like to override a value declared in &lt;code&gt;:host&lt;/code&gt;:&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;/* some app.css - not shadow styles for the component */&lt;/span&gt;
&lt;span class="nt"&gt;my-component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--my-global-token&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="c"&gt;/* overrides values declared in :host */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Set default values on :host
&lt;/h5&gt;

&lt;p&gt;The discussion of overwriting global tokens in &lt;code&gt;:host&lt;/code&gt; leads to the second scenario where you have a component-specific public CSS property (not a global design token) that has a default value. They are essentially the same scenario except you likely don‘t have some global CSS file from the design system setting your component-specific CSS property values and are instead wanting to help out consumer devs needing to customize.&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;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you establish a public CSS property that has a default value, you can only set that default value in your &lt;code&gt;:host&lt;/code&gt; styles. Setting the CSS property value anywhere deeper in your shadow DOM styles could prevent outside styling.&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;/* some shadow root stylesheet */&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;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-shadow-dom-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the CSS property isn‘t actually public anymore. By setting it in a shadow root class you‘ve essentially made that property private. Unless there is some other styling mechanism available like a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part" rel="noopener noreferrer"&gt;CSS part&lt;/a&gt; on the same element, your consumer would not be able to pass in a custom value for &lt;code&gt;--my-public-css-prop&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Make more CSS properties
&lt;/h5&gt;

&lt;p&gt;Another way to ensure that your component can handle complex use cases and features without overriding the value of public CSS properties is to just make more of them! Consider the media query example from above:&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;150ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* public css property */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.some-animating-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(prefers-reduced-motion)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we wanted to fix this example so that the public CSS property isn‘t reset and the actual value used isn‘t a different value than was provided externally we can just add a new CSS property and use that 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="nc"&gt;.some-animating-thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&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;--fast-animation-duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(prefers-reduced-motion)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-duration&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;--fast-animation-reduced-motion-duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our fixed code has 2 public CSS properties, but each of them is never reset or overridden internally and we‘ve removed the calculation. And as long as we write both of these CSS properties down in our extensive and accurate documentation which all of us surely have, the consumers of our component can get the value they set for both scenarios. Naming the extra CSS properties might be a bit difficult depending on the situation, but naming things is always hard no matter what, isn‘t it?&lt;/p&gt;

&lt;h5&gt;
  
  
  Public CSS properties that need multiple values
&lt;/h5&gt;

&lt;p&gt;Let‘s say that you have a component with multiple values for a configuration, but you want to provide a truly custom value for your consumer to override. In the situation where you have a public CSS property that needs to have multiple values but still allow configuration, your only choice is to apply your multiple default values as fallbacks to your public CSS property.&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;/* some shadow root stylesheet */&lt;/span&gt;
&lt;span class="nc"&gt;.size-small&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-public-css-prop&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="nc"&gt;.size-medium&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-public-css-prop&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="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.size-large&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try to set that public CSS property for all your size variants, you‘ll be turning the CSS property basically private again.&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;/* this example won‘t work like you want */&lt;/span&gt;

&lt;span class="c"&gt;/* some shadow root stylesheet */&lt;/span&gt;
&lt;span class="nc"&gt;.size-small&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--my-public-css-prop&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="nl"&gt;padding&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;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.size-medium&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--my-public-css-prop&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;padding&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;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.size-large&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--my-public-css-prop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&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="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--my-public-css-prop&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;
  
  
  Public CSS properties are public API
&lt;/h2&gt;

&lt;p&gt;Fantastic, you‘ve got a robust public API of CSS properties for your components. Surely you wont ever mess it up right? Its not super easy to forget and accidentally set some CSS property you told all your consumers was public and they one day discover they can‘t overwrite. Of course it is super easy to do that! That‘s why we consider public CSS properties truly part of the public API of our components. And what do we do to the public API of our components to make sure they don‘t break?&lt;/p&gt;

&lt;p&gt;We test it. Yep, we unit test the CSS.&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%2Fprwnrqumst7wegyl4zhx.gif" 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%2Fprwnrqumst7wegyl4zhx.gif" alt="Gif of President Obama making a face showing incredulity" width="480" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our consumers can change them, and we designed our component such that they can, so we need to verify that feature doesn‘t unknowingly break.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;My personal advice:&lt;/strong&gt;&lt;br&gt;
Test that your public properties stay public.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That‘s right, dust off that good 'ole &lt;code&gt;window.getComputedStyle()&lt;/code&gt; and verify that your public CSS props actually work. Here‘s what that might look 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="c1"&gt;// sample test for a public CSS property&lt;/span&gt;
&lt;span class="c1"&gt;// with whatever testing tool floats your boat&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Public CSS props&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should use the value as is&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;my-component style="--my-cool-public-css-prop: 69px"&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//nice&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;internalElWherePropIsUsed&lt;/span&gt; &lt;span class="o"&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;renderRoot&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;.prop-used-here&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// lets say the CSS prop is used on the border of the internal element&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualBorder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;internalElWherePropIsUsed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// REMEMBER: DOM access to the style object converts colors to rgb() format&lt;/span&gt;
    &lt;span class="c1"&gt;// so if you‘re setting a CSS prop to a color, just set it to rgb() to begin with&lt;/span&gt;
    &lt;span class="c1"&gt;// to avoid a conversion function&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actualBorder&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;69px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//nice&lt;/span&gt;

  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could even get really fancy and check if your public CSS property gets set even when the external value isn‘t set directly on the &lt;code&gt;:host&lt;/code&gt; element by rendering some global styles into your test setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Public CSS props&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should use the value as is&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="c1"&gt;// your design system friends will haunt your dreams if you use 37px :)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
      &amp;lt;style&amp;gt;:root { --my-cool-public-css-prop: 37px; }&amp;lt;/style&amp;gt;
      &amp;lt;my-component&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// everything else would be the same&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You‘ll have more tests than you originally had, and more tests that aren‘t strictly testing the JS public API, but public API is public API and you‘ll be glad you did test your CSS approach. Public CSS custom property overwriting bugs are hard to spot on your own. Taking the time to codify the feature in your unit tests will prevent your users from discovering them way down the line. Depending on the type of component you have it might be literal years before someone decides to use that CSS property and discovers that it never actually worked right because the component styles reset it.&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>css</category>
    </item>
    <item>
      <title>CSS Parts are saving you from a nightmare</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Wed, 13 Nov 2024 18:58:02 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/css-parts-are-saving-you-1k79</link>
      <guid>https://dev.to/michaelwarren1106/css-parts-are-saving-you-1k79</guid>
      <description>&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%2Fyik6hn7sbz9ikb48u6pa.gif" 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%2Fyik6hn7sbz9ikb48u6pa.gif" alt="Baseball umpire calling a runner safe" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Necessary context
&lt;/h2&gt;

&lt;p&gt;Recently, I was listening to a well known podcast, and the awesome hosts were talking about web components, listing all of the good, bad, and ugly parts about the various web components specs and features.&lt;/p&gt;

&lt;p&gt;Shoutout to Chris Coyier and Dave Rupert for the totally fair episode and great conversation. Give the episode a listen here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shoptalkshow.com/640/" rel="noopener noreferrer"&gt;Shop Talk Show&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anywho, deep in that episode, I was surprised to find that CSS Parts made it onto the baddie list, though I can see some of the reasons behind why folks might think they're not the best. I happen to think that not only are CSS parts pretty good, they are actually saving all of us that use them from a scenario that is WAY more annoying and frustrating than not being able to style every single element inside a web component shadow root exactly like you want.&lt;/p&gt;

&lt;p&gt;Let me explain, but first MORE context!&lt;/p&gt;

&lt;h2&gt;
  
  
  Web components were designed mostly for third party usage
&lt;/h2&gt;

&lt;p&gt;I cannot explicitly verify all of the following to be true, but my personal opinion—created while writing, using and helping spec web component features for the last few years in my day job—is that the existing web component specs and features were all designed to solve the "third party component" use case. That is, web components were designed to support a very specific situation: &lt;strong&gt;a component author creating a tool that other consumers download from the internet and consume in their apps.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I feel that if you look at web component features through the lens of the "third party component" use case, then a lot of the approach that was taken by spec writers and browser implementers make a TON of sense. Take the severity of shadow dom style encapsulation for example. If you are pulling a component off the internet and into your app, isn't it wonderful to not have to worry that that component's css is going to somehow break one of your pages? Isn't it wonderful to not have to concern yourself with the internal structure of a component you didnt write and get to treat that component like a black box and interact with it through a well-defined api surface?&lt;/p&gt;

&lt;p&gt;Most of the trouble with understanding how web components are designed to work is that the industry has moved on since the web component specs were created and these days 99% of the components we use in our apps don't come from external sources. These days, when we think of components, most of the time we're thinking about a "first-party component" that we or our team wrote and not some person across the internet. We don't use that many external components in our applications, so web component APIs designed mostly for that use case seem strange to us, especially when we also try to use web components to create first party components for ourselves.&lt;/p&gt;

&lt;p&gt;So for everything else I'm going to say in this article, assume I'm referring to the "third-party component" situation. Imagine that the component I'm talking about is an npm package that you downloaded and that there's a GitHub readme out there and patch notes and such, but you (or your team) didn't write it yourself for your own app. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;CSS Parts are not a great interface for styling components that you write for yourself or your team and have full control over.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Parts are part of the public API of a web component
&lt;/h2&gt;

&lt;p&gt;Well-designed components have several different ways to interact with them. Great web components have slots for customized HTML, CSS custom properties for customized theming, and CSS Parts for customized styles. The Shop Talk Show fellas discussed CSS Parts with the context being that they are clunky for free form styling, but I wouldn't think about CSS Parts that way. I'd think about them as another public API surface for the component, much the same way as attributes or properties. Folks usually don't worry too much about not being able to create custom properties or attributes for components they pull from the internet. I think CSS Parts are the same idea. The component author designates the pieces of the internal template that are "ok to style" any way you want. So being able to style every single internal element in that shadow dom web component shouldn't necessarily be an expectation.&lt;/p&gt;

&lt;p&gt;And who better to know which parts of some complex template are ok to have any style applied than the component author? If you pull in a component from the internet just to turn around and re-write all the styles, possibly destroying accessibility and such in the process, why did you install the component in the first place? I get the attractiveness of the "I know what I'm doing" selector for shadow roots that lots of folks have advocated for, but in this next section, I'm going to illuminate why I think having a defined styling API surface that is disconnected from the internal DOM structure is actually amazing and should be celebrated for helping all us devs not go crazy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cue old-school movie trailer voice&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In a world where the "I know what I'm doing" selector exists and CSS Parts don't
&lt;/h2&gt;

&lt;p&gt;Lets jump into an imaginary world where CSS Parts don't exist, and some "I know what I'm doing" selector (let's call it &lt;code&gt;/deep/&lt;/code&gt; for the sake of nostalgia) exists and is "the way" that you are supposed to style the internal DOM template of some shadow root web component.&lt;/p&gt;

&lt;p&gt;And you need one such component for your app, so you go and download an npm package&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i some-awesome-package@1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and have the following HTML in your application:&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;some-awesome-component&amp;gt;&amp;lt;/some-awesome-component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Some Awesome Component&lt;/code&gt; component has a div in it that is red by default and doesn't use any CSS custom properties. And at first, the red div is perfectly ok!&lt;/p&gt;

&lt;p&gt;But then you decide that you need to change the color of the red div to &lt;code&gt;rebeccapurple&lt;/code&gt;. So you write this css:&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="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;my-overides&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;some-awesome-component&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.the-red-div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rebeccapurple&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;blockquote&gt;
&lt;p&gt;&lt;code&gt;the-red-div&lt;/code&gt; is a terrible name for a class, but hey, no one cares about the internal structure of a shadow dom web component, right? They can't conflict with the outside world, so component authors can write terrible class names if they want to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And because we're in our imaginary world where &lt;code&gt;/deep/&lt;/code&gt; exists, everything works! The red div is now purple and everything is coolio. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You ignore the tiny cognitive dissonance in our brains that &lt;code&gt;.the-red-div&lt;/code&gt; has a color that isn't red. That's a tomorrow problem. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sweet!&lt;/p&gt;

&lt;p&gt;Then the component author adds a feature and publishes a &lt;strong&gt;patch bump&lt;/strong&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%2F4k21g45nu9luunwvhjck.gif" 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%2F4k21g45nu9luunwvhjck.gif" alt="Erupting geyser" width="500" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're busy shipping and along the way, you re-installed an unrelated package and you pulled the latest patch bump of Some Awesome Component and the red div is back again!&lt;/p&gt;

&lt;p&gt;What gives? You go searching the patch notes and you find:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 1.2.4&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [a987s83] Added cool button, fixed a11y issues
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems fine, why did the css break? Changelog notes don't have any clues. So you skim the latest few PRs and don't see anything that jumps out at you.&lt;/p&gt;

&lt;p&gt;So you spin up your application and go checking the shadow root and the red div is a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; now! And the component author has realized that their class name of &lt;code&gt;the-red-div&lt;/code&gt; should have been more generic, and has changed it to &lt;code&gt;colorful&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ok fine, so you edit your css to:&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="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;my-overides&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;some-awesome-component&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nt"&gt;deep&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nc"&gt;.colorful&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rebeccapurple&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it works fine again!&lt;/p&gt;

&lt;p&gt;Then the author releases &lt;strong&gt;another patch bump&lt;/strong&gt; next week.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Jump cut back to the real world&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct CSS selector access to DOM you don't control is a huge footgun
&lt;/h2&gt;

&lt;p&gt;See the pattern and therefore problem with direct selector access in to internal templates that you don't own or control?&lt;/p&gt;

&lt;p&gt;Inevitably, changes will be made to the internal template outside of the control of you, the component consumer. And inevitably those change will happen in a way that is almost invisible to you without visually inspecting EVERY SINGLE PIECE of your running app, either manually or with flaky visual regression testing tools. If you are using a component from the internet, then there is a contract of sorts. The component author owns the shadow dom, and you the consumer own the &lt;code&gt;:host&lt;/code&gt; element (the &lt;code&gt;&amp;lt;some-awesome-component&amp;gt;&lt;/code&gt; HTML tag itself in your code that you write). Crossing that boundary without some sort of contract in place is a guarantee for brittle code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact, this brittleness also happens when doing direct shadow dom querySelectors for DOM elements in JS too. If your app depends on some element to always exist, it won't. If you query into DOM you don't control, one day some element won't be there. Either it will be a different element or have a different class/attribute/id on it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  But CSS Parts have your back
&lt;/h2&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%2Fizfhmsxgvrbyrwnoky4z.jpg" 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%2Fizfhmsxgvrbyrwnoky4z.jpg" alt=" " width="534" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because CSS Parts are simple strings that are not DOM elements themselves, using them shields your application from brittle code. Third party authors are never going to test that some div is ALWAYS a div forever. But if they create a CSS Part, they know that they are creating a public API for the component that can't be removed or changed without a breaking change. And if you use CSS Parts instead of directly querying shadow DOM, then the component author can move that CSS Part around in the shadow dom and your application code will not break.&lt;/p&gt;

&lt;p&gt;Furthermore, because CSS Part names imply relationships, concepts, and features, it would be hard for a component author to refactor a component in such a way as to make the CSS Part change its meaning substantially. So in addition to knowing that your CSS Part using code isn't going to break as the component author makes changes without telling you, you can also be reasonably sure that the CSS Part will always be the same kind of thing in relationship to the overall component. So the styles you apply to that part will be reasonably sure to always "make sense" for that part even as the component evolves over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  One step further
&lt;/h2&gt;

&lt;p&gt;I've seen a few comments from very smart and valuable industry folks about the pain of CSS Parts and that adding direct shadow dom access via selectors would be much more convenient. If nothing every changed shape over time I would wholeheartedly agree. But since components do change over time, I would venture to suppose that direct shadow DOM access will be more painful than having to remember which CSS Parts are available for use.&lt;/p&gt;

&lt;p&gt;Whenever there are discussions about how to cross the boundary between "the app" and the shadow dom inside a component, I would always advocate for some named structural contractual approach like CSS Parts over direct selector access. In my view, anyone that thinks that CSS Parts are clunky and terrible has just not had to go hunting through their entire app looking for all the places a shadow root template has changed without their knowledge :)&lt;/p&gt;

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

&lt;p&gt;CSS Parts are great and when you use them, even though you 100% are a great developer and you know what you are doing, you 100% also don't want the pain of potentially having to tweak all of that custom CSS you wrote every single time there's a patch bump to components you use. You definitely know what you're doing, but all of us want to prevent our apps from randomly breaking and thats what direct shadow DOM styling will do. An "I know what I'm doing" selector will definitely get your styling to work, but it will only be &lt;strong&gt;guaranteed&lt;/strong&gt; to work if you literally never update the package version you styled. If you do update the version you'll have to keep an eye on all of those "I know what I'm doing" styles every single time.&lt;/p&gt;

&lt;p&gt;Imo thats a waste of our precious time. just use CSS Parts and encourage component devs out there to add them to the the components they write.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webcomponents</category>
      <category>css</category>
    </item>
    <item>
      <title>Preventable events: statelessness in stateful components</title>
      <dc:creator>Michael Warren</dc:creator>
      <pubDate>Thu, 11 Nov 2021 20:39:01 +0000</pubDate>
      <link>https://dev.to/michaelwarren1106/preventable-events-statelessness-in-stateful-components-4cpg</link>
      <guid>https://dev.to/michaelwarren1106/preventable-events-statelessness-in-stateful-components-4cpg</guid>
      <description>&lt;p&gt;One of the biggest debates about authoring web components that I've had, both in my own mind and with coworkers is the debate over stateful vs stateless components. Is it better to have a component that manages a bit of its own state so that developers don't have to in their applications, or is it better that components manage no internal state and only use properties provided from the outside application to render.&lt;/p&gt;

&lt;p&gt;There are pros and cons to either side of the question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros &amp;amp; Cons of Stateless components
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Easier to build&lt;/strong&gt;&lt;br&gt;
With the exception of form elements, completely stateless components are super easy to build. Each property has a certain set of allowed values and the component only re-renders when a property is changed, and only uses the outside properties to change what is rendered. Every functionality is exposed via the external API so that the outside world can manipulate it.&lt;/p&gt;

&lt;p&gt;Native form inputs are a little harder to make stateless, because native HTML form inputs automatically have and track their &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;validity&lt;/code&gt; states. Making an input behave as if it were stateless when the native element isn't purely stateless is very tricky.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application state is the only state&lt;/strong&gt;&lt;br&gt;
Since stateless components don't hold any state, the application's state where components are used is the ONLY state. That way, there's never a chance of conflicting state where the component's internal state might be different than the application's state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible implementation for developers&lt;/strong&gt;&lt;br&gt;
Developers that use stateless components have full freedom to do what they need, when they need to, and they know that the component won't be trying to perform any logic or hold onto any internal state that might potentially conflict with the outside application state. Take the closing of a modal window for example:&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;!-- This modal is closed because its `open` boolean attribute isn't present, and it won't open until the `open` attribute is added --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;x-modal&amp;gt;&amp;lt;/x-modal&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- This modal is open because its "open" boolean attribute is present, but it won't close until the `open` attribute is removed programmatically --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;x-modal&lt;/span&gt; &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-modal&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a completely stateless modal window, the developer gets to decide when the modal closes. If they need to do some extra functionality between the user deciding to close the modal and the modal actually closing, the freedom to do that is built in to the implementation strategy of the component.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Developers MUST recreate component state in their application state&lt;/strong&gt;&lt;br&gt;
Imagine a stateless component with a lot of available properties, and imagine a flow where lots of those properties need to be manipulated. Stateless components means that the application's state needs to be created/bound to component properties to manipulate the component in the desired ways. It's essentially a mirror of state that the component &lt;em&gt;could&lt;/em&gt; have, or in some cases, already "does" have internally. It's also more lines of code in the application itself. It can be argued that components are created to encapsulate functionality and that internal state is part of it.&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;!-- someBooleanVariable is application state that basically mirrors `xModal.open` --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;x-modal&lt;/span&gt; &lt;span class="na"&gt;open=&lt;/span&gt;&lt;span class="s"&gt;"${someBooleanVariable}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-modal&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The more properties you need to manipulate in a given UI, the more closely to mirroring the component's state you'll actually be:&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;!-- 'someObject' is basically a shallow clone of xComponent --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;x-component&lt;/span&gt;
   &lt;span class="na"&gt;active=&lt;/span&gt;&lt;span class="s"&gt;"${someObject.active}"&lt;/span&gt;
   &lt;span class="na"&gt;status=&lt;/span&gt;&lt;span class="s"&gt;"${someObject.status}"&lt;/span&gt;
   &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"${someObject.variant}"&lt;/span&gt;
   &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"${someObject.label}"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-component&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it gets worse if you are looping through repeated instances of the same component, like looping through rows in a table and managing each one's properties individually. In that case, your application state would be some array of objects, each one basically being a shallow copy of the component whose state you're managing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Potential loss of consistency in component behavior&lt;/strong&gt;&lt;br&gt;
If each individual developer is completely in control of what each stateless component does, then you stand to risk some loss of consistency in component behavior. If you're making a design system whose main responsibility is consistency in user experience, statelessness might be a hindrance, depending on the component.&lt;/p&gt;

&lt;p&gt;Take a stateless input for example, where it only displays an error state when the &lt;code&gt;error&lt;/code&gt; parameter has a value.&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;x-form-field&lt;/span&gt; &lt;span class="na"&gt;error=&lt;/span&gt;&lt;span class="s"&gt;"Some error message"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-form-field&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now envision that your organization has collectively made the rule that error messages should never be shown to users while they are typing but only after the form field has lost focus (yelling at users to fix an error they are currently trying to fix is bad form ). Our stateless form field above allows developers to show error messages at any time, even while typing. Preventing that behavior in order to preserve the desired user experience goes against the statelessness concept, because the component is doing something it wasn't told to do from the outside, ie - something like "when this form field is focused, never show error messages, regardless of what the &lt;code&gt;error&lt;/code&gt; property is set to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we have both?
&lt;/h2&gt;

&lt;p&gt;Is it possible to have a component be mostly stateful to prevent application developers from needing to essentially clone our components in their application state and also to help keep consistent UX behaviors, but still selectively allow for them to prevent certain stateful behaviors when they need to?&lt;/p&gt;

&lt;h3&gt;
  
  
  Preventable events pattern
&lt;/h3&gt;

&lt;p&gt;Event listeners is one of the main ways that component developers can respond to actions that happen within the boundaries of a web component. When a user clicks something, selects an option, checks a checkbox, chances are, some event is emitted to the outside application that lets that application know what happened, etc.&lt;/p&gt;

&lt;p&gt;I'm sure that lots of folks reading this are probably already familiar with &lt;code&gt;event.preventDefault()&lt;/code&gt; as we've previously used it to do things like prevent the default click event on links or buttons so that we can execute some JS before changing pages, but we can actually harness this function to enable components to be both stateful and stateless when we need them to be.&lt;/p&gt;

&lt;p&gt;Since event listeners are all executed synchronously — that is, every event handler that is established on some DOM element is executed in a synchronous chain (outside in) before our JS code moves on — it is possible to check to see if a particular event was prevented and use that conditional to decide what to do next. In our case, we would check to see if the event was prevented and if so, NOT perform stateful property setting internally.&lt;/p&gt;

&lt;p&gt;Let's look at our modal window example from before but make it a stateful modal window this time. Meaning, that when the user clicks the X button to close the modal, the modal window will close itself without the dev having to manually set the &lt;code&gt;open&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XModal&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;internalModalClose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// modal will close itself when the close button is clicked.&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;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
       ...other modal stuff

       &amp;lt;button class="close-modal" @click="internalModalClose()"&amp;gt;Close X&amp;lt;/button&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This stateful-only approach saves one line of code in the outer application (for every modal instance), but if the developer needs to run some JS between the user clicking the close button and the modal actually closing, there's no way for that to happen.&lt;/p&gt;

&lt;p&gt;But if we change the internal close button click handler to adopt the preventable event pattern, we'll get what we need!&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XModal&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;internalModalClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// prevent the native click event from bubbling so we can emit our custom event&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// create and dispatch our custom event&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;closeEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close-button-clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;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;closeEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    

    &lt;span class="c1"&gt;// this if block will only execute AFTER all event handlers for the closeEvent have been executed&lt;/span&gt;
    &lt;span class="c1"&gt;// so its safe to check here to see if the event has been defaultPrevented or not&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;closeEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultPrevented&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// perform our stateful activity ONLY if closeEvent hasn't been defaultPrevented.&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;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
       ...other modal stuff

       &amp;lt;button class="close-modal" @click="internalModalClose()"&amp;gt;Close X&amp;lt;/button&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then when our mostly stateful component gets used&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;!-- some-page.html--&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;x-modal&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;close-button-clicked=&lt;/span&gt;&lt;span class="s"&gt;"handleModalClose()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-modal&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;span class="nf"&gt;handleModalClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// now the modal won't close itself automatically&lt;/span&gt;
  &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nx"&gt;some&lt;/span&gt; &lt;span class="nx"&gt;stuff&lt;/span&gt;

  &lt;span class="c1"&gt;// set the open prop to false to close the modal when ready&lt;/span&gt;
  &lt;span class="nx"&gt;xModal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this approach, it enables a component to be stateful, but also allow certain "escape hatches" for developers to take control in a stateless way.&lt;/p&gt;

&lt;p&gt;Even the conceptual idea of "preventing the default behavior" fits semantically. You the component developer are allowing your component consumers the ability to prevent the default stateful behavior in a predictable way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Library function
&lt;/h3&gt;

&lt;p&gt;If you find yourself constantly dispatching custom events that you want to all be preventable, this approach is easily turned into a library or helper function to create and dispatch a preventable event and automatically check to see if that event is &lt;code&gt;defaultPrevented&lt;/code&gt; before executing a callback.&lt;/p&gt;

&lt;p&gt;Here's an example of a generic preventable event factory function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultEventOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;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="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="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventEmitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatchElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualEventOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;defaultEventOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actualEventOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;dispatchElement&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultPrevented&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// if the event isn't prevented, run the callback function with the dispatchElement as `this` so class references in the callback will work&lt;/span&gt;
      &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatchElement&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;and here's how that library function would get used in a component:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XModal&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;LitElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;emitPreventable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eventEmitter&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="kr"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;internalModalClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emitPreventable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close-modal-clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="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;// won't execute unless the event isn't defaultPrevented&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;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`
       ...other modal stuff

       &amp;lt;button class="close-modal" @click="internalModalClose()"&amp;gt;Close X&amp;lt;/button&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This approach isn't applicable everywhere. It will only help with event-based features, which mostly centers around user interaction, so I wouldn't advertise this approach as enabling a component to be fully stateful AND fully stateless at the same time. Its not even a 50/50 mix of the two. If you want to make stateful components and you use an event-based strategy, this approach will enable you to provide more flexibility, but not necessarily ultimate flexibility.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>html</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
