A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The failed promise of Web Components (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://lea.verou.me/2020/09/the-failed-promise-of-web-components/">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>The failed promise of Web Components</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://lea.verou.me/2020/09/the-failed-promise-of-web-components/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>Web Components had so much potential to empower HTML to do more, and make web development more accessible to non-programmers and easier for programmers. Remember how exciting it was every time we got new shiny HTML elements that actually <em>do stuff</em>? Remember how exciting it was to be able to do sliders, color pickers, dialogs, disclosure widgets straight in the HTML, without having to include any widget libraries?</p>
  71. <p>The promise of Web Components was that we’d get this convenience, but for a much wider range of HTML elements, developed much faster, as nobody needs to wait for the full spec + implementation process. We’d just include a script, and boom, we have more elements at our disposal!</p>
  72. <p>Or, that was the idea. Somewhere along the way, the space got flooded by JS frameworks aficionados, who revel in complex APIs, overengineered build processes and dependency graphs that look like the roots of a banyan tree.</p>
  73. <figure class="wp-block-image"><img src="https://live.staticflickr.com/2025/32441377780_e3acf6de12_b.jpg" alt=""><figcaption>This is what the roots of a Banyan tree look like. <a href="https://www.flickr.com/photos/79721788@N00/32441377780/">Photo by David Stanley on Flickr (CC-BY)</a>. </figcaption></figure>
  74. <p><span id="more-3147"></span></p>
  75. <p>Perusing the components on <a href="https://www.webcomponents.org/">webcomponents.org</a> fills me with anxiety, and I’m perfectly comfortable writing JS — I write JS for a living! What hope do those who can’t write JS have? Using a custom element from the directory often needs to be preceded by a ritual of npm flugelhorn, import clownshoes, build quux, all completely unapologetically because “here is my truckload of dependencies, yeah, what”. Many steps are even omitted, likely because they are “obvious”. Often, you wade through the maze only to find the component doesn’t work anymore, or is not fit for your purpose.</p>
  76. <p>Besides setup, the main problem is that HTML is not treated with the appropriate respect in the design of these components. They are not designed as closely as possible to standard HTML elements, but <em>expect</em> JS to be written for them to do anything. HTML is simply treated as a shorthand, or worse, as merely a marker to indicate where the element goes in the DOM, with all parameters passed in via JS. I recall <a href="https://adactio.com/articles/12839#webcomponents">a wonderful talk by Jeremy Keith</a> a few years ago about this very phenomenon, where he discussed <a href="https://shop.polymer-project.org/">this e-shop Web components demo by Google</a>, which is the poster child of this practice. These are the entire contents of its <code>&lt;body&gt;</code> element:</p>
  77. <pre class="wp-block-code language-markup"><code>&lt;body&gt;
  78. &lt;shop-app unresolved=""&gt;SHOP&lt;/shop-app&gt;
  79. &lt;script src="node_assets/@webcomponents/webcomponentsjs/webcomponents-loader.js"&gt;&lt;/script&gt;
  80. &lt;script type="module" src="src/shop-app.js"&gt;&lt;/script&gt;
  81. &lt;script&gt;window.performance&amp;&amp;performance.mark&amp;&amp;performance.mark("index.html");&lt;/script&gt;
  82. &lt;/body&gt;</code></pre>
  83. <p>If this is how Google is leading the way, how can we hope for contributors to design components that follow established HTML conventions?</p>
  84. <p>Jeremy criticized this practice from the aspect of backwards compatibility: when JS is broken or not enabled, or the browser doesn’t support Web Components, the entire website is blank. While this is indeed a serious concern, my primary concern is one of <strong>usability</strong>: <strong>HTML is a lower barrier to entry language</strong>. Far more people can write HTML than JS. Even for those who do eventually write JS, it often comes after spending years writing HTML &amp; CSS.</p>
  85. <p>If components are designed in a way that requires JS, this excludes thousands of people from using them. And even for those who <em>can</em> write JS, HTML is often easier: you don’t see many people rolling their own sliders or using JS-based ones once <code>&lt;input type="range"&gt;</code> became widely supported, right?</p>
  86. <p>Even when JS is unavoidable, it’s not black and white. A well designed HTML element can reduce the amount and complexity of JS needed to a minimum. Think of the <code><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog">&lt;dialog&gt;</a></code> element: it usually does require *some* JS, but it’s usually rather simple JS. Similarly, the <code>&lt;video&gt;</code> element is perfectly usable just by writing HTML, and has a comprehensive JS API for anyone who wants to do fancy custom things.</p>
  87. <p>The other day I was looking for a simple, dependency free, tabs component. You know, the canonical example of something that is easy to do with Web Components, the example 50% of tutorials mention. I didn’t even care what it looked like, it was for a testing interface. I just wanted something that is small and works like a normal HTML element. Yet, it proved so hard I ended up writing my own!</p>
  88. <h3><a href="#can-we-fix-this" aria-hidden="true" class="aal_anchor" id="can-we-fix-this"><svg aria-hidden="true" class="aal_svg" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Can we fix this?</h3>
  89. <p>I’m not sure if this is a design issue, or a documentation issue. Perhaps for many of these web components, there are easier ways to use them. Perhaps there are vanilla web components out there that I just can’t find. Perhaps I’m looking in the wrong place and there is another directory somewhere with different goals and a different target audience. </p>
  90. <p>But if not, and if I’m not alone in feeling this way, we need a directory of web components with strict inclusion criteria:</p>
  91. <ul><li><strong>Plug and play.</strong> No dependencies, no setup beyond including one <code>&lt;script&gt;</code> tag. If a dependency is absolutely <em>needed</em> (e.g. in a map component it doesn’t make sense to draw your own maps), the component loads it automatically if it’s not already loaded.</li><li>Syntax and API follows <a href="https://www.smashingmagazine.com/2017/02/designing-html-apis/"><strong>conventions established by built-in HTML elements</strong></a> and anything that <em>can</em> be done without the component user writing JS, <em>is</em> doable without JS, per <a href="https://www.w3.org/2001/tag/doc/leastPower.html">the W3C principle of least power</a>.</li><li><strong>Accessible by default</strong> via sensible ARIA defaults, just like normal HTML elements.</li><li><strong>Themable</strong> via <code><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part">::part()</a></code>, selective inheritance and custom properties. Very minimal style by default. Normal CSS properties should just “work” to the the extent possible.</li><li><strong>Only one component of a given type</strong> in the directory, that is <strong>flexible</strong> and <strong>extensible</strong> and continuously iterated on and improved by the community. Not 30 different sliders and 15 different tabs that users have to wade through. No branding, no silos of “component libraries”. Only elements that are designed as closely as possible to what a browser would implement in every way the current technology allows.</li></ul>
  92. <p>I would be up for working on this if others feel the same way, since that is not a project for one person to tackle. <em>Who’s with me?</em></p>
  93. <p><strong>UPDATE:</strong> Wow this post blew up! Thank you all for your interest in participating in a potential future effort. I’m currently talking to stakeholders of some of the existing efforts to see if there are any potential collaborations before I go off and create a new one. <a href="https://twitter.com/leaverou">Follow me on Twitter to hear about the outcome</a>!</p>
  94. </article>
  95. <hr>
  96. <footer>
  97. <p>
  98. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  99. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  100. </svg> Accueil</a> •
  101. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  102. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  103. </svg> Suivre</a> •
  104. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  105. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  106. </svg> Pro</a> •
  107. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  108. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  109. </svg> Email</a> •
  110. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  111. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  112. </svg> Légal</abbr>
  113. </p>
  114. <template id="theme-selector">
  115. <form>
  116. <fieldset>
  117. <legend><svg class="icon icon-brightness-contrast">
  118. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  119. </svg> Thème</legend>
  120. <label>
  121. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  122. </label>
  123. <label>
  124. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  125. </label>
  126. <label>
  127. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  128. </label>
  129. </fieldset>
  130. </form>
  131. </template>
  132. </footer>
  133. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  134. <script>
  135. function loadThemeForm(templateName) {
  136. const themeSelectorTemplate = document.querySelector(templateName)
  137. const form = themeSelectorTemplate.content.firstElementChild
  138. themeSelectorTemplate.replaceWith(form)
  139. form.addEventListener('change', (e) => {
  140. const chosenColorScheme = e.target.value
  141. localStorage.setItem('theme', chosenColorScheme)
  142. toggleTheme(chosenColorScheme)
  143. })
  144. const selectedTheme = localStorage.getItem('theme')
  145. if (selectedTheme && selectedTheme !== 'undefined') {
  146. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  147. }
  148. }
  149. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  150. window.addEventListener('load', () => {
  151. let hasDarkRules = false
  152. for (const styleSheet of Array.from(document.styleSheets)) {
  153. let mediaRules = []
  154. for (const cssRule of styleSheet.cssRules) {
  155. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  156. continue
  157. }
  158. // WARNING: Safari does not have/supports `conditionText`.
  159. if (cssRule.conditionText) {
  160. if (cssRule.conditionText !== prefersColorSchemeDark) {
  161. continue
  162. }
  163. } else {
  164. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  165. continue
  166. }
  167. }
  168. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  169. }
  170. // WARNING: do not try to insert a Rule to a styleSheet you are
  171. // currently iterating on, otherwise the browser will be stuck
  172. // in a infinite loop…
  173. for (const mediaRule of mediaRules) {
  174. styleSheet.insertRule(mediaRule.cssText)
  175. hasDarkRules = true
  176. }
  177. }
  178. if (hasDarkRules) {
  179. loadThemeForm('#theme-selector')
  180. }
  181. })
  182. </script>
  183. </body>
  184. </html>