Test page

Here I am playing around with an approach for generating (mostly static) pages via Facet Web Components that nontheless also work without JavaScript. This is achieved by a middleware in the server that finds all Facet component definitions in the returned HTML and inserts them as a declarative shadow dom into the usages. While doing so, it strips out any <script> elements, and inserts transitively used components. If and when the JavaScript loads, all the declarative shadow DOMs get replaced by the Facet component, restoring interactivity.

To prevent having to include the component definitions into every page, the server also appends a common HTML file into before processing. This leads to absolutely awful HTML, that is still parsed correctly.

The following is an example of a simple interactive custom element.

It's implementation looks something like this:

<template component="press-counter">
  Pressed
  <button>
    +
    <script on="click">
      const span = root.querySelector("#counter");
      span.innerText = +span.innerText + 1;
    </script>
  </button> <span id="counter">0</span> times.
</template>

It can be invoked like any other HTML element, i.e. <press-counter></press-counter>.

Global mixins are also supported, allthough the prepend attribute is ignored. This is used to consolidate the style between my components with their Shadow DOM and the page specific content in the lightdom. For this, I defined the style as usual with a <link rel="stylesheet"> element in the <head>, and <style> elements in a global mixin, which imports the same stylesheet as the <head>. This looks like this:

<template mixin="style" global>
  <style>
    @import "/style.css" layer;
  </style>
</template>

The @import at-rule is usually hard to use correctly, because it leads to resources which are only discovered late in the rendering of the page. Here we are loading exactly the same stylesheet which was already loaded in the <head> which is then simply re-used without any extra network requests.