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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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>Using the platform (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://elisehe.in/2021/08/22/using-the-platform">
  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>Using the platform</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-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://elisehe.in/2021/08/22/using-the-platform" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>I recently came across a series of articles by Daniel Kehoe where he introduces <a href="https://tutorials.yax.com/articles/build-websites-the-yax-way/quicktakes/what-is-the-yax-way.html"><em>The Stackless Way</em></a>, an optimistic take on web development that proposes we “use the platform” (modern features built into the language) instead of frameworks and build tools that keep getting replaced every few years.</p>
  71. <p>It was good timing. While I’m a front-end developer at heart, I’ve rarely had the luxury of focusing on it full time. I’ve been dipping in and out of JavaScript, never fully caught up, always trying to navigate the ecosystem all over again each time a project came up. And framework fatigue is real!</p>
  72. <p>So, instead of finally getting into Rollup to replace an ancient Browserify build on an old codebase (which could also really use that upgrade from Polymer to LitElement…), I decided to go “stackless”. I took a long-time idea for a motion design project and built it using nothing but features native to the browser: vanilla JS, ES6 modules and web components.</p>
  73. <p>Working on a codebase with no dependencies has been a way of rediscovering exactly what I get for free in 2021, and what value I’m adding by bringing frameworks, transpilers and bundlers to the mix. I’d like to share what I learned (and what I needed to unlearn) in the process.</p>
  74. <h3 id="the-project">The project</h3>
  75. <p>The project itself, <a href="https://schematics.elisehe.in"><em>Schematics: A Love Story</em></a>, is a collection of animated diagrams from a visual poetry book by the same name. The animations dotted throughout this write-up are examples from the project: SVG created programmatically with modular, vanilla JavaScript, encapsulated in a web component, sitting inside a framework-free codebase.</p>
  76. <figure class="image">
  77. <picture>
  78. <source srcset="https://d33wubrfki0l68.cloudfront.net/0d2f110a4e442af3495fdc556c2dbd92accc9255/5a69e/assets/post-assets/stackless/fig14-desktop.gif" media="(min-width: 750px)"></source>
  79. <img src="https://d33wubrfki0l68.cloudfront.net/d20fa5f0b97d59400afd9bae5611ceb9b1437c93/ed6db/assets/post-assets/stackless/fig14-mobile.gif" alt="Figure 14: A line spiralling upwards along a the time axis in a 3-dimensional coordinate system.">
  80. </picture>
  81. <figcaption>
  82. A diagram reproduced from <a href="https://julianhibbard.com">Julian Hibbard’s</a> <em>Schematics: A Love Story</em>, handcrafted with modern JavaScript.<br><a href="https://schematics.elisehe.in">See full collection</a>
  83. </figcaption>
  84. </figure>
  85. <h2 id="the-stackless-way">The Stackless Way</h2>
  86. <p>Daniel Kehoe is not alone in his push-back on the complexity of the modern web. Frank Chimero’s <a href="https://frankchimero.com/blog/2018/everything-easy/"><em>Everything Easy is Hard Again</em></a> gets to me every time, and, more recently, I enjoyed <a href="https://medium.com/codex/youre-missing-out-on-vanilla-js-91aceec917d6">this lighthearted rant</a> about the lack of appreciation for vanilla JS.</p>
  87. <p>Appeals for external JS dependencies to be added with thoughtful intent have become common, but some schools of thought question the concept of single-page apps as a whole: server-side rendering is still a thing, after all. Pages can also be pre-built into a fully static site and served from a CDN (see <a href="https://jamstack.org/what-is-jamstack/">Jamstack</a>). These approaches recognise that we can move some of the complexity currently managed by front-end frameworks elsewhere on the stack.</p>
  88. <p>But Kehoe’s series of articles on going stackless feels a little different. It’s not just about JavaScript — it’s a wholehearted devotion to the native features of the web as a platform (Routing? Just make sure every URL matches a .html file!)</p>
  89. <p>There are limits to this kind of purism, of course. The Stackless Way, to me, is less of a realistic approach to building production web apps and more of a learning and introspection tool, a way to take a step back and fall in love with the platform again.</p>
  90. <p>Being all about motion design, my own project relied on many a <code class="language-plaintext highlighter-rouge">setTimeout</code> and page transitions — a single-page app, really… just handcrafted. The two main technologies that made it possible are ES6 modules and web components, which, along with module CDNs, form the pillars of The Stackless Way.</p>
  91. <h2 id="es6-modules">ES6 modules</h2>
  92. <p>If you’ve been developing for the web for some time, you might remember doing this:</p>
  93. <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span>
  94. <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/js/vendor/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  95. <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/js/main.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  96. <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/js/subscribe.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  97. <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/js/gallery.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  98. <span class="c">&lt;!-- Many more script tags whose specific ordering was a source of bugs and frustration --&gt;</span>
  99. <span class="nt">&lt;/head&gt;</span>
  100. </code></pre></div></div>
  101. <p>Listing all required scripts separately in the document <code class="language-plaintext highlighter-rouge">&lt;head&gt;</code> was all we could do given the lack of support for a native import/export mechanism in JavaScript. This was, of course, suboptimal:</p>
  102. <ul>
  103. <li>each script tag initiates a HTTP request;</li>
  104. <li>there is no namespacing (all scripts exist in the global scope);</li>
  105. <li>the order of execution is linear, and maps directly onto the ordering of the script tags.</li>
  106. </ul>
  107. <p>To combat this, approaches to modularity in JavaScript have proliferated over the years (AMD, UMD, CommonJS), and along with them, build tools and bundlers to convert the modular code into something that the browser understands.</p>
  108. <p>ECMAScript Modules (also referred to as ESM or ES6 Modules) is the first <em>native</em> standard for modules in JavaScript. I emphasise <em>native</em> here because that means we can ditch the build tools and bundlers in favour of <code class="language-plaintext highlighter-rouge">&lt;script type=“module”&gt;</code> in HTML and <code class="language-plaintext highlighter-rouge">import</code>/<code class="language-plaintext highlighter-rouge">export</code> in JS:</p>
  109. <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span>
  110. <span class="c">&lt;!-- Single entry point for a dependency graph of any depth --&gt;</span>
  111. <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"module"</span> <span class="na">src=</span><span class="s">"main.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  112. <span class="nt">&lt;/head&gt;</span>
  113. </code></pre></div></div>
  114. <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.js</span>
  115. <span class="k">import</span> <span class="nx">Gallery</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./Gallery.js</span><span class="dl">"</span><span class="p">;</span>
  116. <span class="k">import</span> <span class="nx">SubscriptionForm</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./SubscriptionForm.js</span><span class="dl">"</span><span class="p">;</span>
  117. <span class="c1">// Gallery.js</span>
  118. <span class="k">import</span> <span class="nx">Swipe</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./helpers/gestures.js</span><span class="dl">"</span><span class="p">;</span>
  119. <span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">Gallery</span> <span class="p">{}</span>
  120. </code></pre></div></div>
  121. <p><strong>Native support for modularity is the most important step towards a build-free codebase</strong>. If I had access to only one <a href="http://es6-features.org/">ES6 feature</a> for the rest of my life, I’m confident that modules would take me most of the way there when it comes to well-structured native JavaScript.</p>
  122. <p>And not having to build your JS app is magical. Setting up the directory structure for <em>Schematics</em>, I was giddy with excitement skipping <code class="language-plaintext highlighter-rouge">npm run build</code> and seeing my source files mirrored — as is! — in the browser. It reminded me of when I first began building websites and double-clicking <code class="language-plaintext highlighter-rouge">index.html</code> was enough to see your work in the browser.</p>
  123. <p>You do need a local server if you’re using ES6 modules (so double-clicking an HTML file might be a thing of the past for good). But there’s no lag between editing your code and seeing changes in the browser, and the source code you see in the inspector is exactly what you typed into your editor (no sourcemaps!).</p>
  124. <p>Nevermind the faster edit-compile-debug cycle — this speaks directly to Chimero’s concern about the <a href="https://frankchimero.com/blog/2018/everything-easy/">lack of legibility in today’s codebases as an obstacle to learning the craft</a>:</p>
  125. <blockquote>
  126. <p>Before, the websites could explain themselves; now, someone needs to walk you through it. Illegibility comes from complexity without clarity. I believe that the legibility of the source is one of the most important properties of the web. […] the best way to help someone <em>write</em> markup is to make sure they can <em>read</em> markup. (<a href="https://frankchimero.com/blog/2018/everything-easy/">Frank Chimero</a>)</p>
  127. </blockquote>
  128. <figure class="image">
  129. <picture>
  130. <source srcset="https://d33wubrfki0l68.cloudfront.net/5d8af1fbafa273e967948ac4fef631cb65230ef8/c1472/assets/post-assets/stackless/fig18-desktop.gif" media="(min-width: 750px)"></source>
  131. <img src="https://d33wubrfki0l68.cloudfront.net/17fab52ccc9fc287c87cb7cd801de4d371344419/e0067/assets/post-assets/stackless/fig18-mobile.gif" alt="Figure 18: A flow diagram for love. Good? Yes. More? Yes. Do it.">
  132. </picture>
  133. <figcaption>
  134. A diagram reproduced from <a href="https://julianhibbard.com">Julian Hibbard’s</a> <em>Schematics: A Love Story</em>, handcrafted with modern JavaScript.<br><a href="https://schematics.elisehe.in">See full collection</a>
  135. </figcaption>
  136. </figure>
  137. <p>I recommend reading Kehoe’s <a href="https://tutorials.yax.com/articles/javascript/import/index.html"><em>Javascript import explained</em></a> or Ayodeji’s <a href="https://medium.com/backticks-tildes/introduction-to-es6-modules-49956f580da"><em>Introduction to ES6 modules</em></a> for a historic overview of approaches to modularity, how we got to ESM, and how to write your own modules.</p>
  138. <h3 id="the-future-of-app-bundles">The future of app bundles</h3>
  139. <p>Just because you don’t strictly need a build tool to <em>run</em> your code, doesn’t mean you shouldn’t use one for production builds to optimise performance. I’m referring here not to transpilation, but to minification, mangling, tree-shaking, etc.</p>
  140. <p>I’ve been bundling JavaScript into a single <code class="language-plaintext highlighter-rouge">app.js</code> file for such a long time that my first instinct was to concatenate everything into a single script on <em>Schematics</em>, too. This, of course, will not work with ES6 modules — the concept of files is what creates boundaries between different modules, and there is no way to specify more than one module per file.</p>
  141. <p>Luckily, you don’t need to bundle your ES6 modules into a single file to improve performance. Let me repeat: <strong>you don’t need to bundle your ES6 modules into a single file to improve performance</strong>.</p>
  142. <p>Here’s why:</p>
  143. <ol>
  144. <li><code class="language-plaintext highlighter-rouge">&lt;script type=“module"&gt;</code> requests are deferred by default: they won’t block document parsing.</li>
  145. <li>Serving all modules from separate source files is great for caching, as the modules unaffected by some change can continue being retrieved from the cache. When you serve a single bundle, any one change will invalidate the whole bundle.</li>
  146. <li>You can lazy load parts of your code by making use of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports">dynamic imports</a> — importing modules during runtime at the point where you actually need them.</li>
  147. <li>You can preload critical modules with <a href="https://developers.google.com/web/updates/2017/12/modulepreload"><code class="language-plaintext highlighter-rouge">modulepreload</code></a>. This starts parsing and compiling the linked modules immediately, off the main thread.</li>
  148. <li>Finally, HTTP2 may in the future allow us to resolve the entire dependency graph from the first <code class="language-plaintext highlighter-rouge">&lt;script type=“module”&gt;</code> request, and send all required files back in a single response. For the time being, this requires bespoke logic written on the server side.</li>
  149. </ol>
  150. <p>And don’t forget about bundle size:</p>
  151. <blockquote>
  152. <p>If you inspect the output code generated by most popular bundlers, you’ll find a lot of boilerplate whose only purpose is to dynamically load other code and manage dependencies, but none of that would be needed if we just used modules with import and export statements! (<a href="https://philipwalton.com/articles/using-native-javascript-modules-in-production-today">Philip Walton</a>)</p>
  153. </blockquote>
  154. <p>I still have some work to do to go against my first instinct to bundle. It’s not uncommon for a SPA to have hundreds of JavaScript files, and the prospect of serving them all separately seems counterintuitive. Even having read up on all the reasons why I don’t need to bundle my modules, I occasionally hesitated before separating some logic into its own file to avoid the extra request.</p>
  155. <figure class="image">
  156. <picture>
  157. <source srcset="https://d33wubrfki0l68.cloudfront.net/ea47b365638fd85ec7952a63fafb1997530db2b2/b2b8c/assets/post-assets/stackless/fig20-desktop.gif" media="(min-width: 750px)"></source>
  158. <img src="https://d33wubrfki0l68.cloudfront.net/3835a734f4f22ce2dddee7654ebd561f17e23042/cceca/assets/post-assets/stackless/fig20-mobile.gif" alt="Figure 20: An illustration of the propagation of sound waves across arrays of vertical lines.">
  159. </picture>
  160. <figcaption>
  161. A diagram reproduced from <a href="https://julianhibbard.com">Julian Hibbard’s</a> <em>Schematics: A Love Story</em>, handcrafted with modern JavaScript.<br><a href="https://schematics.elisehe.in">See full collection</a>
  162. </figcaption>
  163. </figure>
  164. <p>For more details and interesting discussion around ES6 module load performance and app bundles, see <a href="https://esdiscuss.org/topic/fwd-are-es6-modules-in-browsers-going-to-get-loaded-level-by-level">this thread on the ECMAScript Discussion Archives</a>. The spec is not set in stone yet, and there are plenty of ideas floating around, some more daring than others (my personal favourite is the proposal to serve the entire module dependency graph as a .zip file).</p>
  165. <blockquote>
  166. <p>There’s plenty of ongoing module work happening in Chrome, though, so we’re getting closer to giving bundlers their well-earned rest! (<a href="https://developers.google.com/web/updates/2017/12/modulepreload">Sérgio Gomes</a>)</p>
  167. </blockquote>
  168. <p>I also recommend reading <a href="https://philipwalton.com/articles/using-native-javascript-modules-in-production-today"><em>Using Native JavaScript Modules in Production Today</em></a> by Philip Walton. It explains how to serve optimised module files alongside a standard fallback single-file bundle for older browsers.</p>
  169. <p>Finally, if you’re looking for an alternative to webpack that supports native modules, check out <a href="https://vitejs.dev">Vite</a> or <a href="https://rollupjs.org/">Rollup</a>.</p>
  170. <h2 id="web-components">Web components</h2>
  171. <p><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">Web Components</a> is an umbrella term for several native technologies (Custom Elements, Shadow DOM and HTML templates) that let us bundle the markup and dynamic behaviour of an element into a reusable piece of code, on a high level no different to the React or Vue component. As of May 2021, <a href="https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/"><em>All the Ways to Make a Web Component</em></a> lists 55 variants of a hypothetical <code class="language-plaintext highlighter-rouge">&lt;my-counter&gt;</code> component for comparison across bundle size, coding style and performance. Native web components, of course, stand out because they have no external dependencies (and, consequently, an unmatched tiny bundle size).</p>
  172. <p>Here are some of my own impressions of web components as compared to external frameworks when it comes to DX and architecture.</p>
  173. <h3 id="shadow-dom-and-template">Shadow DOM and <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code></h3>
  174. <p>The shadow DOM, a way of keeping the internals of a component inaccessible to the main document, is a neat feature in theory, but a natural use case for it didn’t really arise when working on <em>Schematics</em>. I’ve played around with the shadow DOM in the past as part of a Polymer (now LitElement) project, and I mostly remember it creating more problems than it solved, especially when it came to styling. I can see the shadow DOM being useful in cases where a component is reused across many sites and in unpredictable contexts (such as elements in a UI library); for your standard <code class="language-plaintext highlighter-rouge">&lt;site-specific-header-dropdown&gt;</code>, it doesn’t add much value.</p>
  175. <p>The <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;slot&gt;</code> elements (the latter only useful if you’re using the shadow DOM) are presented as a means of adding hidden markup inside JS, but they feel clunky if you’re used to something like JSX. Additionally, the <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> tag <a href="https://stackoverflow.com/a/53317973">seems counter-inuitive if the aim is to create portable components</a> that can be included in an app with a single import. When declared in the HTML, any JavaScript making use of the <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> makes assumptions about what’s available to it in the main document. Indeed, at times the <code class="language-plaintext highlighter-rouge">index.html</code> in Schematics felt like a dumping ground for various <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code>s, rather than a neat overview of the page structure.</p>
  176. <p><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/HTML_Imports">HTML Imports</a> was a proposal that might have allowed us to package JS and HTML together into a single bundle, making <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> the star of the show. Unfortunately, the feature never took off.</p>
  177. <p>Another <a href="https://github.com/whatwg/html/issues/2254">proposed feature</a> is a means to add variables, simple statements and event handlers to nodes in a <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> tag. The gist of it is similar to what you’d currently see in front-end frameworks:</p>
  178. <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template</span> <span class="na">id=</span><span class="s">"card"</span><span class="nt">&gt;</span>
  179. <span class="nt">&lt;card&gt;</span>
  180. <span class="nt">&lt;h2&gt;</span>{{title}}<span class="nt">&lt;/h2&gt;</span>
  181. <span class="nt">&lt;div&gt;</span>{{description}}<span class="nt">&lt;/div&gt;</span>
  182. <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/card/{{id}}"</span><span class="nt">&gt;</span>Read more<span class="nt">&lt;/a&gt;</span>
  183. <span class="nt">&lt;button</span> <span class="na">handler=</span><span class="s">"onEdit"</span><span class="nt">&gt;</span>Edit<span class="nt">&lt;/button&gt;</span>
  184. <span class="nt">&lt;/card&gt;</span>
  185. <span class="nt">&lt;/temlate&gt;</span>
  186. </code></pre></div></div>
  187. <p>Being accustomed to built-in data-binding in frameworks, until the variables above are automatically updated, the feature feels half-baked. As it stands, it’s difficult to gauge the full, declarative markup of a component in one place, as update logic is scattered throughout the class with query selectors, <code class="language-plaintext highlighter-rouge">innerHTML</code>, document fragments and the like.</p>
  188. <p>If you’d like variable and event handler support in <code class="language-plaintext highlighter-rouge">&lt;template&gt;</code> now, GitHub, who <a href="https://github.blog/2021-05-04-how-we-use-web-components-at-github/">use vanilla web components internally</a>, provide <a href="https://github.com/github/template-parts">a polyfill</a> for the minimum viable bits of the proposal. As for data binding, Danny Moerkerke describes one approach to implementing it yourself: <a href="https://medium.com/swlh/https-medium-com-drmoerkerke-data-binding-for-web-components-in-just-a-few-lines-of-code-33f0a46943b3"><em>Data binding for Web Components in just a few lines of code</em></a>.</p>
  189. <figure class="image">
  190. <picture>
  191. <source srcset="https://d33wubrfki0l68.cloudfront.net/d85e81c35756d05361c49b19e45743d3f40f6567/75587/assets/post-assets/stackless/fig36-desktop.gif" media="(min-width: 750px)"></source>
  192. <img src="https://d33wubrfki0l68.cloudfront.net/d0c8e2853dbe0b9a57cdfc78906026bff470b269/dd0d7/assets/post-assets/stackless/fig36-mobile.gif" alt="Figure 36: A swinging pendulum.">
  193. </picture>
  194. <figcaption>
  195. A diagram reproduced from <a href="https://julianhibbard.com">Julian Hibbard’s</a> <em>Schematics: A Love Story</em>, handcrafted with modern JavaScript.<br><a href="https://schematics.elisehe.in">See full collection</a>
  196. </figcaption>
  197. </figure>
  198. <h3 id="extending-native-elements">Extending native elements</h3>
  199. <p>The web components feature I was most excited about, and one that a third-party framework inherently cannot provide, is <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#customized_built-in_elements">the ability to extend native HTML elements</a>. This allows you to inherit any built-in behaviour for that element in your component, including accessibility-specific properties. Unfortunatley, <a href="https://dev.to/lkraav/comment/ad06">Apple have decided not to support customizable elements in Safari</a>.</p>
  200. <p>This is disappointing. Using front-end frameworks, we often implement our own, more snazzy versions of native HTML elements. Input elements especially, such as dropdowns and radio buttons, are tricky to style using CSS and often end up reproduced with JS. But reproducing the required ARIA attributes, keyboard navigation and the like requires extra effort — something we would get for free with customizable elements.</p>
  201. <h3 id="components-all-the-way-down">Components all the way down?</h3>
  202. <p>While ES modules sparked excitement for the future, I was left underwhelmed by the prospect of native web components replacing traditional front-end frameworks. In hindsight, weighing up web components against JS frameworks was missing the point of the exercise. It’s not about making more of your JavaScript vanilla; it’s spotting opportunities to forego JavaScript in the first place.</p>
  203. <p>Being a React user, attaching my <code class="language-plaintext highlighter-rouge">App.js</code> root component to a <code class="language-plaintext highlighter-rouge">&lt;div id=“app”&gt;</code> root node and working down the tree, in smaller components, is how I’m used to thinking about SPAs. Though you <em>can</em> attach your component onto individual DOM nodes (and you could easily mix and match frameworks, too), the more standard approach is to let the framework drive the whole page.</p>
  204. <p>This will quickly fall apart when using web components. At the level of the atomic UI element, they shine; for the business logic and user flows, you should defer to a different part of your stack. As for static content — why render it with JavaScript if raw markup does the job perfectly?</p>
  205. <p>And so, I caught my mindset shifting from an app that’s just one big component to <em>not</em> using components by default, breaking the link between component and framework in the process.</p>
  206. <p>In the case of <em>Schematics</em>, I ended up with just a couple of main custom elements sitting neatly in the middle of native HTML tags. A simplified illustration of the idea:</p>
  207. <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;body&gt;</span>
  208. <span class="nt">&lt;header&gt;&lt;/header&gt;</span>
  209. <span class="nt">&lt;aside&gt;&lt;/aside&gt;</span>
  210. <span class="nt">&lt;main&gt;</span>
  211. <span class="nt">&lt;schematics-figure&gt;&lt;/schematics-figure&gt;</span>
  212. <span class="nt">&lt;schematics-figure-toolbar&gt;&lt;/schematics-figure-toolbar&gt;</span>
  213. <span class="nt">&lt;/main&gt;</span>
  214. <span class="nt">&lt;footer&gt;&lt;/footer&gt;</span>
  215. <span class="nt">&lt;/body&gt;</span>
  216. </code></pre></div></div>
  217. <p>It seems obvious in hindsight that a component on a website should encapsulate the behaviour and appearance of some bit of UI. But, as <a href="https://dev.to/redbar0n/what-happened-to-components-being-just-a-visual-thing-22hc">this article nicely puts it</a>, with the proliferation of JS frameworks, “we’ve landed over in the ditch on the other side of the road: putting all sorts of behaviour into the same JSX-denoted structure as Components”.</p>
  218. <blockquote>
  219. <p>“React renders your UI and responds to events” was <a href="https://youtu.be/x7cQ3mrcKaY?t=79">how it was introduced</a>. Not: “React is a way to transform and execute all your logic in a hierarchy of declarative markup”, as it has devolved into in many cases. (<a href="https://dev.to/redbar0n/what-happened-to-components-being-just-a-visual-thing-22hc">Magne</a>)</p>
  220. </blockquote>
  221. <figure class="image">
  222. <picture>
  223. <source srcset="https://d33wubrfki0l68.cloudfront.net/6d77f50ef261b769e44844c897ce310990d23906/7d632/assets/post-assets/stackless/fig43-desktop.gif" media="(min-width: 750px)"></source>
  224. <img src="https://d33wubrfki0l68.cloudfront.net/f544521286fe6c3c943b2adea827785c25f4ba91/21968/assets/post-assets/stackless/fig43-mobile.gif" alt="Figure 43: A 3D cube erratically rotating and changing size.">
  225. </picture>
  226. <figcaption>
  227. A diagram reproduced from <a href="https://julianhibbard.com">Julian Hibbard’s</a> <em>Schematics: A Love Story</em>, handcrafted with modern JavaScript.<br><a href="https://schematics.elisehe.in">See full collection</a>
  228. </figcaption>
  229. </figure>
  230. <hr>
  231. <p>The above thoughts are mostly based on first impressions. If you’re interested in a more detailed review of the state of the web components spec, I recommend <a href="https://webreflection.medium.com/about-web-components-cc3e8b4035b0">this excellent article by Andrea Giammarchi</a> which takes a more critical look of the feature in the context of the 30-year history of the web.</p>
  232. <h2 id="the-future-of-stackless">The future of stackless</h2>
  233. <p><em>Schematics</em> was a simple project as far as architecture goes, and web components and ES6 modules took me most of the way there. I can’t see myself building a complex app without a framework. Fully static sites, too, are simpler to build using a generator like Jekyll: something like markup reuse across pages is impossible when you only have the browser and text editor to work with (the humble <code class="language-plaintext highlighter-rouge">&lt;iframe&gt;</code> might disagree with me here…).</p>
  234. <p>Technical limitations aside, an issue I became aware of during this exercise was a lack of standards in vanilla JavaScript. Because we’re so used to the framework dictating how to structure our codebase, there aren’t many established guidelines should you decide to handcraft your JavaScript. Never mind the high-level architecture — even the way you define a class, use static variables, or implement composition all have a range of approaches to them. Web components fill in the gap when it comes to the UI; perhaps if more developers dared to go framework-free (when appropriate!) discussions around best practices would be more productive.</p>
  235. <h2 id="know-the-platform">Know the platform</h2>
  236. <p>Developing web apps without frameworks or build tools is not an end goal in and of itself. As Daniel Kehoe put it in the stackless newsletter:</p>
  237. <blockquote>
  238. <p>I don’t think we’ll be talking about “stackless” in a few years. It’s just going to be part of any web developer’s professional bag of tricks.</p>
  239. </blockquote>
  240. <p>I’d love to see that happen. I have a special disdain for beginner JavaScript tutorials that have you run <code class="language-plaintext highlighter-rouge">create-react-app</code> as the first step, and this exercise has only strengthened my conviction that every beginner programmer should get to grips with HTML, CSS and vanilla JS before delving into frameworks. Features native to the web are what all frameworks share, and knowing the platform makes for a stronger foundation in the face of change.</p>
  241. <hr>
  242. <h3 id="postscriptum-stackless-stylesheets">Postscriptum: Stackless stylesheets?</h3>
  243. <p>I’m so used to writing LESS or SCSS, or, more recently, setting up a set of PostCSS plugins, that <em>not</em> having a preprocessor didn’t cross my mind at first.</p>
  244. <p>Indeed, Kehoe encourages the use of CSS frameworks and/or preprocessors <a href="https://tutorials.yax.com/articles/the-yax-way/2.html">“until libraries of custom UI elements are more broadly available”</a>.</p>
  245. <p>But such a big part of the appeal of stackless for me was running <em>all</em> source code in the browser as is — having a preprocessing step for CSS would ruin that. So, in the spirit of the exercise I limited myself to vanilla CSS.</p>
  246. <p>I persisted with this right up until the first media query came along, and things began to look nasty. Yes, modern CSS does get you 90% there; custom properties are a real workhorse (don’t forget, no preprocessor can give you properties that update during runtime, or that you can set/get via JavaScript). But the lack of native support for nested selectors (along with the parent selector) I cannot live without, and that extra 10% makes a world’s difference.</p>
  247. <p>Stackless CSS isn’t there yet. Use a preprocessor.</p>
  248. </article>
  249. <hr>
  250. <footer>
  251. <p>
  252. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  253. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  254. </svg> Accueil</a> •
  255. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  256. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  257. </svg> Suivre</a> •
  258. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  259. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  260. </svg> Pro</a> •
  261. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  262. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  263. </svg> Email</a> •
  264. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  265. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  266. </svg> Légal</abbr>
  267. </p>
  268. <template id="theme-selector">
  269. <form>
  270. <fieldset>
  271. <legend><svg class="icon icon-brightness-contrast">
  272. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  273. </svg> Thème</legend>
  274. <label>
  275. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  276. </label>
  277. <label>
  278. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  279. </label>
  280. <label>
  281. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  282. </label>
  283. </fieldset>
  284. </form>
  285. </template>
  286. </footer>
  287. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  288. <script>
  289. function loadThemeForm(templateName) {
  290. const themeSelectorTemplate = document.querySelector(templateName)
  291. const form = themeSelectorTemplate.content.firstElementChild
  292. themeSelectorTemplate.replaceWith(form)
  293. form.addEventListener('change', (e) => {
  294. const chosenColorScheme = e.target.value
  295. localStorage.setItem('theme', chosenColorScheme)
  296. toggleTheme(chosenColorScheme)
  297. })
  298. const selectedTheme = localStorage.getItem('theme')
  299. if (selectedTheme && selectedTheme !== 'undefined') {
  300. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  301. }
  302. }
  303. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  304. window.addEventListener('load', () => {
  305. let hasDarkRules = false
  306. for (const styleSheet of Array.from(document.styleSheets)) {
  307. let mediaRules = []
  308. for (const cssRule of styleSheet.cssRules) {
  309. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  310. continue
  311. }
  312. // WARNING: Safari does not have/supports `conditionText`.
  313. if (cssRule.conditionText) {
  314. if (cssRule.conditionText !== prefersColorSchemeDark) {
  315. continue
  316. }
  317. } else {
  318. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  319. continue
  320. }
  321. }
  322. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  323. }
  324. // WARNING: do not try to insert a Rule to a styleSheet you are
  325. // currently iterating on, otherwise the browser will be stuck
  326. // in a infinite loop…
  327. for (const mediaRule of mediaRules) {
  328. styleSheet.insertRule(mediaRule.cssText)
  329. hasDarkRules = true
  330. }
  331. }
  332. if (hasDarkRules) {
  333. loadThemeForm('#theme-selector')
  334. }
  335. })
  336. </script>
  337. </body>
  338. </html>